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

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19.  
  20. #include "xp.h"
  21. #include "layout.h"
  22. #include "laylayer.h"
  23. #include "libi18n.h"
  24. #include "edt.h"
  25. #include "layers.h"
  26. #include <ctype.h> /* For lo_CharacterClassOf */
  27.  
  28. #ifndef XP_TRACE
  29. # define XP_TRACE(X) fprintf X
  30. #endif
  31.  
  32. #ifdef TEST_16BIT
  33. #define XP_WIN16
  34. #endif /* TEST_16BIT */
  35.  
  36. #ifdef XP_WIN16
  37. #define SIZE_LIMIT              32000
  38. #endif /* XP_WIN16 */
  39.  
  40. #ifdef PROFILE
  41. #pragma profile on
  42. #endif
  43.  
  44. #ifdef DEBUG
  45. void lo_ValidatePosition(MWContext *context, LO_Position* position);
  46. void lo_ValidateSelection(MWContext *context, LO_Selection* selection);
  47. void lo_ValidatePosition2(MWContext *context, LO_Element* element, int32 position);
  48. void lo_ValidateSelection2(MWContext *context, LO_Element* elementB, int32 positionB, LO_Element* elementE, int32 positionE);
  49.  
  50. #define LO_ASSERT_POSITION(context, p) lo_ValidatePosition(context, p)
  51. #define LO_ASSERT_SELECTION(context, s) lo_ValidateSelection(context, s)
  52. #define LO_ASSERT_POSITION2(context, e, p) lo_ValidatePosition2(context, e, p)
  53. #define LO_ASSERT_SELECTION2(context, eb, pb, ee, pe) lo_ValidateSelection2(context, eb, pb, ee, pe)
  54.  
  55. #else
  56.  
  57. #define LO_ASSERT_POSITION(context, p) {}
  58. #define LO_ASSERT_SELECTION(context, s) {}
  59. #define LO_ASSERT_POSITION2(context, e, p) {}
  60. #define LO_ASSERT_SELECTION2(context, eb, pb, ee, pe) {}
  61.  
  62. #endif
  63.  
  64. static void
  65. lo_bump_position(MWContext *context, lo_DocState *state,
  66.         LO_Element *sel, int32 pos,
  67.         LO_Element **new_sel, int32 *new_pos, Bool forward);
  68.  
  69. /*
  70.  * Utility functions to convert between insert point and selection end.
  71.  * Returns FALSE if the conversion can't be done.
  72.  * (Only happens when the insert point is off the beginning of the document, or the
  73.  * selection end is off the end of the document.) In those cases, the input position is
  74.  * returned unchanged.
  75.  *
  76.  */
  77.  
  78. Bool lo_ConvertInsertPointToSelectionEnd(MWContext* context, lo_DocState * state,
  79.     LO_Element** element, int32* position);
  80. Bool lo_ConvertSelectionEndToInsertPoint(MWContext* context, lo_DocState * state,
  81.     LO_Element** element, int32* position);
  82.  
  83. void lo_HighlightSelect(MWContext *context, lo_DocState *state,
  84.     LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
  85.     Bool on);
  86. void lo_SetSelect(MWContext *context, lo_DocState *state,
  87.     LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
  88.     Bool on);
  89. void
  90. lo_StartNewSelection(MWContext *context, lo_DocState * state,
  91.     LO_Element* eptr, int32 position);
  92.  
  93. void
  94. lo_ExtendSelectionToPosition2(MWContext *context, lo_TopState* top_state, lo_DocState *state, LO_Element* eptr, int32 position);
  95.  
  96. /* Makes sure *eptr points to an editable element. If *eptr doesn't, searches towards
  97.  * the end of the document.
  98.  * If no editiable element is found, the function
  99.  * returns FALSE.
  100.  */
  101. Bool
  102. lo_EnsureEditableSearchNext(MWContext *context, lo_DocState *state, LO_Element** eptr);
  103.  
  104. Bool
  105. lo_EnsureEditableSearchNext2(MWContext *context, lo_DocState *state, LO_Element** eptr, int32* ePositionPtr);
  106.  
  107. /* Same as lo_EnsureEditableSearchNext, except searches towards the start of document.
  108.  */
  109.  
  110. Bool
  111. lo_EnsureEditableSearchPrev(MWContext *context, lo_DocState *state, LO_Element** eptr);
  112.  
  113. Bool
  114. lo_EnsureEditableSearchPrev2(MWContext *context, lo_DocState *state, LO_Element** eptr, int32* ePositionPtr);
  115.  
  116. Bool
  117. lo_EnsureEditableSearch(MWContext *context, lo_DocState* state, LO_Position* p, Bool forward);
  118.  
  119. Bool
  120. lo_EnsureEditableSearch2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward);
  121.  
  122. Bool
  123. lo_IsValidEditableInsertPoint2(MWContext *context, lo_DocState* state, LO_Element* eptr, int32 ePositionPtr);
  124.  
  125.  
  126. /* Normalizes the selection, if editing is turned on. Returns TRUE if the selection
  127.  * is non empty.
  128.  */
  129.  
  130. Bool lo_NormalizeSelection(MWContext *context);
  131.  
  132. /*
  133.  * Return FALSE if the selection point can't be normalized.
  134.  * (i.e. if it's at the end of the document.)
  135.  */
  136.  
  137. Bool
  138. lo_NormalizeSelectionPoint(MWContext *context, lo_DocState *state, LO_Element** pEptr, int32* pPosition);
  139.  
  140. void
  141. lo_NormalizeSelectionEnd(MWContext *context, lo_DocState *state, LO_Element** pEptr, int32* pPosition);
  142.  
  143. int32
  144. lo_GetTextAttrMask(LO_Element* eptr);
  145.  
  146. LO_AnchorData*
  147. lo_GetAnchorData(LO_Element* eptr);
  148.  
  149. LO_Element*
  150. lo_GetNeighbor(LO_Element* element, Bool forward);
  151.  
  152. int32
  153. lo_GetElementEdge(LO_Element* element, Bool forward);
  154.  
  155. int32
  156. lo_GetMaximumInsertPointPosition(LO_Element* eptr);
  157.  
  158. int32
  159. lo_GetLastCharEndPosition(LO_Element* eptr);
  160.  
  161. int32
  162. lo_GetLastCharBeginPosition(LO_Element* eptr);
  163.  
  164. int32
  165. lo_IncrementPosition(LO_Element* eptr, int32 position);
  166.  
  167. int32
  168. lo_DecrementPosition(LO_Element* eptr, int32 position);
  169.  
  170. Bool
  171. lo_IsEndOfParagraph2(MWContext* context, LO_Element* element, int32 position);
  172.  
  173. Bool
  174. lo_FindDocumentEdge(MWContext* context, lo_DocState *state, LO_Position* edge, Bool select, Bool forward);
  175.  
  176. Bool
  177. lo_IsEdgeOfDocument2(MWContext* context, lo_DocState *state, LO_Element* element, int32 position, Bool forward);
  178.  
  179. Bool
  180. lo_IsEdgeOfDocument(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward);
  181.  
  182. void
  183. lo_SetInsertPoint(MWContext *context, lo_TopState *top_state, LO_Element* eptr, int32 position, CL_Layer *layer);
  184.  
  185. /*
  186.  * Implements the UI policy for a click on an element. Returns TRUE if the result is an insertion point.
  187.  */
  188.  
  189. Bool lo_ProcessClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, Bool requireCaret, CL_Layer *layer);
  190. Bool lo_ProcessDoubleClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, CL_Layer *layer);
  191. Bool lo_ProcessAnchorClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result);
  192.  
  193. Bool
  194. lo_SelectAnchor(MWContext* context, lo_DocState *state, LO_Element* eptr);
  195.  
  196. void
  197. lo_SetSelection(MWContext *context, LO_Selection* selection, Bool extendingStart);
  198.  
  199. void
  200. lo_FullSetSelection(MWContext *context, lo_DocState * state,
  201.     LO_Element* start, int32 start_pos,
  202.     LO_Element* end, int32 end_pos, Bool extendStart);
  203.  
  204. /* Get various selection parts, expressed as insert points. */
  205.  
  206. void
  207. lo_GetAnchorPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition);
  208. void
  209. lo_GetExtensionPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition);
  210.  
  211. Bool lo_FindBestPositionInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX, 
  212.             int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY );
  213. Bool lo_FindClosestUpDown_Cell(MWContext *pContext, lo_DocState* state, 
  214.                                LO_CellStruct* cell, int32 x, int32 y,
  215.                           Bool bForward, int32* ret_x, int32* ret_y);
  216. LO_Element *
  217. lo_search_element_list_WideMatch(MWContext *context, LO_Element *eptr, LO_Element* eEndPtr, int32 x, int32 y,
  218.                                  Bool bForward);
  219.  
  220. Bool lo_EditableElement( int iType )
  221. {
  222.     if( iType == LO_TEXT
  223.         || iType == LO_TEXTBLOCK 
  224.         || iType == LO_IMAGE 
  225.         || iType == LO_HRULE
  226.         || iType == LO_LINEFEED
  227.         || iType == LO_FORM_ELE )
  228.     {
  229.         return TRUE;
  230.     }
  231.     else
  232.     {
  233.         return FALSE;
  234.     }
  235. }
  236.  
  237. PRIVATE
  238. int32
  239. lo_ElementToCharOffset2(MWContext *context, lo_DocState *state,
  240.     LO_Element *element, int32 x, int32* returnCharStart, int32* returnCharEnd)
  241. {
  242.  
  243.     LO_TextStruct *text_ele;
  244.     LO_TextInfo text_info;
  245.     int32 orig_len;
  246.     int32 xpos;
  247.     int32 cpos;
  248.     int32 start, end;
  249.     int16 charset;
  250.     int32 startX;
  251.  
  252.     if ((element == NULL)||(element->type != LO_TEXT))
  253.     {
  254.         return(-1);
  255.     }
  256.  
  257.     text_ele = (LO_TextStruct *)element;
  258.     if ((text_ele->text == NULL)||(text_ele->text_len <= 0))
  259.     {
  260.         return -1;
  261.     }
  262.  
  263.     startX = text_ele->x + text_ele->x_offset;
  264.     xpos = x - startX;
  265.     if (xpos < 0)
  266.     {
  267.         return -1;
  268.     }
  269.  
  270.     if (xpos >= text_ele->width && returnCharStart == NULL)
  271.     {
  272.         return lo_GetLastCharEndPosition(element);
  273.     }
  274.  
  275.     start = 0;
  276.     end = lo_GetLastCharEndPosition(element);
  277.  
  278.     /* Make a guess as to where to start searching, assuming characters are
  279.      * all roughly the same width.
  280.      */
  281.  
  282.     if ( text_ele->width > 0 )
  283.     {
  284.         cpos = xpos * text_ele->text_len / text_ele->width;
  285.     }
  286.     else
  287.     {
  288.         cpos = 0;
  289.     }
  290.     if (cpos > end)
  291.     {
  292.         cpos = end;
  293.     }
  294.     orig_len = text_ele->text_len;
  295.  
  296.     charset = text_ele->text_attr->charset;
  297.     if (INTL_CharSetType(charset) == SINGLEBYTE)
  298.     {    
  299.         while (start < end)
  300.         {
  301.             XP_ASSERT(cpos >= 0);
  302.             text_ele->text_len = (intn) cpos + 1;
  303.             FE_GetTextInfo(context, text_ele, &text_info);
  304.             if (xpos > text_info.max_width)
  305.             {
  306.                 start = cpos + 1;
  307.             }
  308.             else
  309.             {
  310.                 end = cpos;
  311.             }
  312.             cpos = (start + end) / 2;
  313.         }
  314.  
  315.         /* Find character width by trickery */
  316.         text_ele->text_len = (intn) cpos;
  317.         FE_GetTextInfo(context, text_ele, &text_info);
  318.         if ( returnCharStart != NULL ) *returnCharStart = startX + text_info.max_width;
  319.         text_ele->text_len = (intn) cpos+1;
  320.         FE_GetTextInfo(context, text_ele, &text_info);
  321.         if ( returnCharEnd != NULL ) *returnCharEnd = startX + text_info.max_width;
  322.        }
  323.     else  
  324.     {             /* slow multibyte finding, I feel sad we can't use the
  325.                    beautiful single byte algorithm above
  326.                 */
  327.         int32 prev_xpos, last_xpos, last_pos;
  328.         char *tptr;
  329.  
  330.         PA_LOCK(tptr, char *, text_ele->text);
  331.         cpos = 0;
  332.         prev_xpos = 0;
  333.         last_xpos = text_ele->width ;
  334.         last_pos = end ;
  335.         while ((start <= end) && (cpos <= end))
  336.         {
  337.             text_ele->text_len = (intn) cpos ;
  338.             FE_GetTextInfo(context, text_ele, &text_info);
  339.             if (xpos > text_info.max_width)
  340.             {
  341.                 prev_xpos = text_info.max_width ;
  342.                 start = text_ele->text_len;
  343.             }
  344.             else    /* since it goes char by char, finding ends here */
  345.             {
  346.                 last_xpos = text_info.max_width ;
  347.                 end = cpos;
  348.                 break ;
  349.             }
  350.             /* go to next char */
  351.             cpos = start +
  352.                 (int32)INTL_CharLen(charset,
  353.                 (unsigned char *)(tptr + start));
  354.         }
  355.         cpos = start ;
  356.         if (cpos > last_pos)
  357.             cpos = last_pos ;
  358.  
  359.         PA_UNLOCK(text_ele->text);
  360.  
  361.         if ( returnCharStart != NULL ) *returnCharStart = startX + prev_xpos;
  362.         if ( returnCharEnd != NULL) *returnCharEnd = startX + last_xpos;
  363.     }
  364.  
  365.     text_ele->text_len = (intn) orig_len;
  366.  
  367.     return(cpos);
  368. }
  369.  
  370. /*
  371.  * For clients that don't care about the bounds of what they hit.
  372.  */
  373. PRIVATE
  374. int32
  375. lo_ElementToCharOffset(MWContext *context, lo_DocState *state,
  376.     LO_Element *element, int32 x)
  377. {
  378.     return lo_ElementToCharOffset2(context, state, element, x, NULL, NULL);
  379. }
  380.  
  381. void
  382. LO_StartSelection(MWContext *context, int32 x, int32 y, CL_Layer *layer)
  383. {
  384. #if 0
  385.     int32 doc_id;
  386.     lo_TopState *top_state;
  387.     lo_DocState *state;
  388.     LO_Element *eptr;
  389.     int32 position;
  390.     int32 ret_x, ret_y;
  391.  
  392.     /*
  393.      * Get the unique document ID, and retreive this
  394.      * documents layout state.
  395.      */
  396.     doc_id = XP_DOCID(context);
  397.     top_state = lo_FetchTopState(doc_id);
  398.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  399.     {
  400.         return;
  401.     }
  402.     state = top_state->doc_state;
  403.  
  404.     LO_HighlightSelection(context, FALSE);
  405.     state->selection_start = NULL;
  406.     state->selection_start_pos = 0;
  407.     state->selection_end = NULL;
  408.     state->selection_end_pos = 0;
  409.  
  410.     position = 0;
  411.     eptr = lo_XYToDocumentElement(context, state, x, y, FALSE, TRUE, TRUE,
  412.         &ret_x, &ret_y);
  413.     if (eptr != NULL)
  414.     {
  415.         position = lo_ElementToCharOffset(context, state, eptr, ret_x);
  416.         if (position < 0)
  417.         {
  418.             position = 0;
  419.         }
  420.     }
  421.  
  422.     state->selection_new = eptr;
  423.     state->selection_new_pos = position;
  424. #endif
  425.  
  426.     LO_Click(context, x, y, ! EDT_IS_EDITOR(context),
  427.              layer); /* Only allow selections in editor */
  428. }
  429.  
  430. /*
  431.  * Used for right-clicks.
  432.  */
  433. void LO_SelectObject( MWContext *context, int32 x, int32 y, CL_Layer *layer) 
  434. {
  435.     int32 doc_id;
  436.     lo_TopState *top_state;
  437.     lo_DocState *state;
  438.     LO_HitResult result;
  439.  
  440.     /*
  441.      * Get the unique document ID, and retreive this
  442.      * documents layout state.
  443.      */
  444.     doc_id = XP_DOCID(context);
  445.     top_state = lo_FetchTopState(doc_id);
  446.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  447.     {
  448.         return;
  449.     }
  450.     state = top_state->doc_state;
  451.  
  452.     LO_Hit(context, x, y, FALSE, &result, layer);
  453.  
  454.     if ( ! lo_ProcessAnchorClick(context, top_state, state, &result ) ) {
  455.         lo_ProcessClick(context, top_state, state, &result, FALSE, layer );
  456.     }
  457. }
  458.  
  459.  
  460. void LO_StartSelectionFromElement( MWContext *context, LO_Element *eptr, int32 new_pos, CL_Layer *layer )
  461. {
  462.     int32 doc_id;
  463.     lo_TopState *top_state;
  464.     lo_DocState *state;
  465.  
  466.     /*
  467.      * Get the unique document ID, and retreive this
  468.      * documents layout state.
  469.      */
  470.     doc_id = XP_DOCID(context);
  471.     top_state = lo_FetchTopState(doc_id);
  472.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  473.     {
  474.         return;
  475.     }
  476.     state = top_state->doc_state;
  477.  
  478.     if ( eptr ) {
  479.         if ( ! lo_EnsureEditableSearchPrev2(context, state, &eptr, &new_pos) )
  480.         {
  481.             XP_ASSERT(FALSE);
  482.             return;
  483.         }
  484.         LO_ASSERT_POSITION2(context, eptr, new_pos);
  485.     }
  486.  
  487.     LO_HighlightSelection(context, FALSE);
  488.  
  489.     state->selection_start = NULL;
  490.     state->selection_start_pos = 0;
  491.     state->selection_end = NULL;
  492.     state->selection_end_pos = 0;
  493.  
  494.     state->selection_new = eptr;
  495.     state->selection_new_pos = new_pos;
  496.     state->selection_layer = layer;
  497. }
  498.  
  499. #ifdef DEBUG
  500.  
  501. PRIVATE
  502. void
  503. LO_DUMP_SELECTIONSTATE(MWContext *context)
  504. {
  505.     int32 doc_id;
  506.     lo_TopState *top_state;
  507.     lo_DocState *state;
  508.  
  509.     /*
  510.      * Get the unique document ID, and retreive this
  511.      * documents layout state.
  512.      */
  513.     doc_id = XP_DOCID(context);
  514.     top_state = lo_FetchTopState(doc_id);
  515.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  516.     {
  517.         return;
  518.     }
  519.     state = top_state->doc_state;
  520.  
  521.     XP_TRACE(("selection 0x%08x %d 0x%08x %d new 0x%08x %d extending_start %d\n",
  522.         state->selection_start,
  523.         state->selection_start_pos,
  524.         state->selection_end,
  525.         state->selection_end_pos,
  526.         state->selection_new,
  527.         state->selection_new_pos,
  528.         state->extending_start));
  529. }
  530.  
  531. PRIVATE
  532. void
  533. LO_DUMP_INSERT_POINT(char* s, LO_Element* element, int32 position)
  534. {
  535.     if ( element )
  536.     {
  537.         XP_TRACE(("%s %d:%d.%d ", s, element->type, element->lo_any.ele_id, position));
  538.     }
  539.     else
  540.     {
  541.         XP_TRACE(("%s - NIL", s));
  542.     }
  543. }
  544.  
  545. PRIVATE
  546. void
  547. LO_DUMP_POSITION(char* s, LO_Position* position)
  548. {
  549.     LO_DUMP_INSERT_POINT(s, position->element, position->position);
  550. }
  551.  
  552. PRIVATE
  553. void
  554. LO_DUMP_SELECTION2(char* s, LO_Element* a, int32 ap, LO_Element* b, int32 bp)
  555. {
  556. /*    const char* kElementCodes = "?tlhi?????????????????";*/
  557.     XP_TRACE(("%s %d:%d.%d %d:%d.%d ", s,
  558.         a->type, a->lo_any.ele_id, ap,
  559.         b->type, b->lo_any.ele_id, bp));
  560. }
  561.  
  562. PRIVATE
  563. void
  564. LO_DUMP_SELECTION(char* s, LO_Selection* selection)
  565. {
  566.     LO_DUMP_SELECTION2(s, selection->begin.element, selection->begin.position,
  567.         selection->end.element, selection->end.position);
  568. }
  569.  
  570. PRIVATE
  571. void
  572. LO_DUMP_HIT_RESULT(LO_HitResult* result)
  573. {
  574.     switch ( result->type )
  575.     {
  576.     case LO_HIT_UNKNOWN:
  577.         XP_TRACE(("Unknown"));
  578.         break;
  579.     case LO_HIT_LINE:
  580.         {
  581.             char* s;
  582.             switch ( result->lo_hitLine.region )
  583.             {
  584.             case LO_HIT_LINE_REGION_BEFORE:
  585.                 {
  586.                     s = "before line";
  587.                 }
  588.                 break;
  589.             case LO_HIT_LINE_REGION_AFTER:
  590.                 {
  591.                     s = "after line";
  592.                 }
  593.                 break;
  594.             default:
  595.                     s = "LO_HIT_LINE Bad region";
  596.                 break;
  597.             }
  598.             LO_DUMP_SELECTION(s, & result->lo_hitLine.selection);
  599.         }
  600.         break;
  601.     case LO_HIT_ELEMENT:
  602.         {
  603.             char* s;
  604.             switch ( result->lo_hitElement.region )
  605.             {
  606.             case LO_HIT_ELEMENT_REGION_BEFORE:
  607.                 {
  608.                    s = "before element";
  609.                 }
  610.                 break;
  611.             case LO_HIT_ELEMENT_REGION_MIDDLE:
  612.                 {
  613.                    s = "middle element";
  614.                 }
  615.                 break;
  616.             case LO_HIT_ELEMENT_REGION_AFTER:
  617.                 {
  618.                     s = "after element";
  619.                 }
  620.                 break;
  621.             default:
  622.                 {
  623.                     s = "LO_HIT_ELEMENT unknown region";
  624.                 }
  625.                 break;
  626.             }
  627.             LO_DUMP_POSITION(s, & result->lo_hitElement.position);
  628.         }
  629.         break;
  630.     default:
  631.         {
  632.             XP_TRACE(("LO_HIT unknown result "));
  633.         }
  634.         break;
  635.     }
  636. }
  637.  
  638. #endif
  639.  
  640. Bool
  641. LO_IsSelected(MWContext *context)
  642. {
  643.     int32 doc_id;
  644.     lo_TopState *top_state;
  645.     lo_DocState *state;
  646.  
  647.     /*
  648.      * Get the unique document ID, and retreive this
  649.      * documents layout state.
  650.      */
  651.     doc_id = XP_DOCID(context);
  652.     top_state = lo_FetchTopState(doc_id);
  653.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  654.     {
  655.         return FALSE;
  656.     }
  657.     state = top_state->doc_state;
  658.  
  659.     return (state->selection_start != NULL);
  660. }
  661.  
  662. Bool
  663. LO_IsSelectionStarted(MWContext *context)
  664. {
  665.     int32 doc_id;
  666.     lo_TopState *top_state;
  667.     lo_DocState *state;
  668.  
  669.     /*
  670.      * Get the unique document ID, and retreive this
  671.      * documents layout state.
  672.      */
  673.     doc_id = XP_DOCID(context);
  674.     top_state = lo_FetchTopState(doc_id);
  675.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  676.     {
  677.         return FALSE;
  678.     }
  679.     state = top_state->doc_state;
  680.  
  681.     return (state->selection_new != NULL);
  682. }
  683.  
  684. PRIVATE
  685. void lo_JiggleToMakeEditable( MWContext *context, 
  686.                             lo_DocState *state,
  687.                             LO_Element** ppElement, 
  688.                             int32 *pPosition,
  689.                             Bool bForward)
  690. {
  691.    /* If it's not an editable element, move to find one that is.
  692.      * We don't call the ensure routines because we don't want to
  693.      * select end-of-paragraph linefeeds here.
  694.      *
  695.      */
  696.     LO_Element* element = *ppElement;
  697.     int32 position = *pPosition;
  698.  
  699.     if ( (! EDT_IS_EDITOR(context)) || (element && element->lo_any.edit_element) ) {
  700.         return;
  701.     }
  702.  
  703.     while( element && ! element->lo_any.edit_element ){
  704.         LO_Element* newElement;
  705.         int32 newPosition;
  706.         lo_bump_position(context, state, element, position, &newElement, &newPosition, bForward);
  707.         if ( element == newElement && position == newPosition ) {
  708.             /* We got stuck */
  709.             element = NULL;
  710.             break;
  711.         }
  712.         element = newElement;
  713.         position = newPosition;
  714.     }
  715.     if ( ! element ) {
  716.         /* Ran off end of document */
  717.         element = *ppElement;
  718.         position = *pPosition;
  719.         while( element && ! element->lo_any.edit_element ){
  720.             LO_Element* newElement;
  721.             int32 newPosition;
  722.             lo_bump_position(context, state, element, position, &newElement, &newPosition, !bForward);
  723.             if ( element == newElement && position == newPosition ) {
  724.                 /* We got stuck */
  725.                 element = NULL;
  726.                 break;
  727.         }
  728.         element = newElement;
  729.         position = newPosition;
  730.         }
  731.     }
  732.     if ( element ) {
  733.         *ppElement = element;
  734.         *pPosition = lo_GetElementEdge(element, !bForward);
  735.     }
  736. }
  737.  
  738. /* This is only called from the editor. As a convienience to the
  739.  * editor, we return the selection as a 1/2 open selection. That
  740.  * means we convert the selection end into an insert point.
  741.  */
  742.  
  743. void LO_GetSelectionEndPoints( MWContext *context, 
  744.                             LO_Element** ppStart, 
  745.                             intn *pStartOffset,
  746.                             LO_Element** ppEnd, 
  747.                             intn *pEndOffset,
  748.                             Bool* pbFromStart,
  749.                             Bool* pbSingleItemSelection )
  750. {
  751.     int32 doc_id;
  752.     lo_TopState *top_state;
  753.     lo_DocState *state;
  754.     LO_Element* startElement;
  755.     int32 startPosition;
  756.     LO_Element* endElement;
  757.     int32 endPosition;
  758.     /*
  759.      * Get the unique document ID, and retreive this
  760.      * documents layout state.
  761.      */
  762.     doc_id = XP_DOCID(context);
  763.     top_state = lo_FetchTopState(doc_id);
  764.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  765.     {
  766.         return;
  767.     }
  768.     state = top_state->doc_state;
  769.  
  770.     startElement = state->selection_start;
  771.     startPosition = state->selection_start_pos;
  772.     /* If it's not an editable element, move backward to find one that is.
  773.      * We don't call the ensure routines because we don't want to
  774.      * select end-of-paragraph linefeeds here.
  775.      *
  776.      * But now that we have tables, we need to bump.
  777.      */
  778.     lo_JiggleToMakeEditable( context, state, &startElement, &startPosition, FALSE);
  779.     if( ppStart ) *ppStart = startElement;
  780.     if( pStartOffset ) *pStartOffset = startPosition;
  781.  
  782.  
  783.     /* Convert the selection end into an insert point. */
  784.     endElement = state->selection_end;
  785.     endPosition = state->selection_end_pos;
  786.     if ( endElement )
  787.     {
  788.         lo_ConvertSelectionEndToInsertPoint(context, state, &endElement, &endPosition);
  789.     }
  790.  
  791.     /* If it's not an editable element, move forward to find one that is.
  792.      * We don't call the ensure routines because we don't want to
  793.      * select end-of-paragraph linefeeds here.
  794.      */
  795.     lo_JiggleToMakeEditable( context, state, &endElement, &endPosition, TRUE);
  796.  
  797.     if( ppEnd ) *ppEnd = endElement;
  798.     if( pEndOffset ) *pEndOffset = endPosition;
  799.  
  800.     if( pbFromStart ) *pbFromStart = state->extending_start;
  801.  
  802.     /* 
  803.      * the editor needs to know if the select is just a single thing like a
  804.      *  image or a HRULE.
  805.     */
  806.     if( pbSingleItemSelection ) *pbSingleItemSelection = (
  807.             startElement
  808.             && startElement->type != LO_TEXT
  809.             && startElement->type != LO_TEXTBLOCK
  810.             && startElement == endElement );
  811.  
  812.     /*
  813.      * For lloyd's edification, if we have a single item seleciton, start_pos 
  814.      *  is 0 and end_pos is 0 or 1
  815.     */
  816.     XP_ASSERT( pbSingleItemSelection && *pbSingleItemSelection 
  817.             ? 
  818.                 startPosition == 0
  819.                 && ( endPosition == 0
  820.                     || endPosition == 1 )
  821.             :    
  822.                 TRUE);
  823. }
  824.     
  825. void LO_GetSelectionNewPoint( MWContext *context, 
  826.                             LO_Element** ppNew, 
  827.                             intn *pNewOffset)
  828. {
  829.     int32 doc_id;
  830.     lo_TopState *top_state;
  831.     lo_DocState *state;
  832.  
  833.     /*
  834.      * Get the unique document ID, and retreive this
  835.      * documents layout state.
  836.      */
  837.     doc_id = XP_DOCID(context);
  838.     top_state = lo_FetchTopState(doc_id);
  839.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  840.     {
  841.         return;
  842.     }
  843.     state = top_state->doc_state;
  844.  
  845.     if( ppNew ) *ppNew = state->selection_new;
  846.     if( pNewOffset ) *pNewOffset = state->selection_new_pos;
  847. }
  848.  
  849. static intn
  850. lo_compare_selections(LO_Element *sel1, int32 pos1,
  851.     LO_Element *sel2, int32 pos2)
  852. {
  853.     if ((sel1 == NULL)||(sel2 == NULL))
  854.     {
  855.         if (sel1 == sel2)
  856.         {
  857.             return(0);
  858.         }
  859.         else if (sel1 == NULL)
  860.         {
  861.             return(-1);
  862.         }
  863.         else
  864.         {
  865.             return(1);
  866.         }
  867.     }
  868.  
  869.     if (sel1->lo_any.ele_id < sel2->lo_any.ele_id)
  870.     {
  871.         return(-1);
  872.     }
  873.     else if (sel1->lo_any.ele_id > sel2->lo_any.ele_id)
  874.     {
  875.         return(1);
  876.     }
  877.     else if (sel1->lo_any.ele_id == sel2->lo_any.ele_id)
  878.     {
  879.         if (pos1 < pos2)
  880.         {
  881.             return(-1);
  882.         }
  883.         else if (pos1 > pos2)
  884.         {
  885.             return(1);
  886.         }
  887.         else if (pos1 == pos2)
  888.         {
  889.             return(0);
  890.         }
  891.     }
  892. #ifdef DEBUG
  893.     assert (FALSE);
  894. #endif
  895.     return(-1);
  896. }
  897.  
  898. PRIVATE
  899. intn
  900. lo_ComparePositions(LO_Position* a, LO_Position* b)
  901. {
  902.     return lo_compare_selections( a->element, a->position, b->element, b->position);
  903. }
  904.  
  905. PRIVATE
  906. LO_Element*
  907. lo_BoundaryJumpingNext(MWContext *context, lo_DocState *state, LO_Element *eptr)
  908. {
  909.     int32 last_id;
  910.     LO_Element *cell_parent;
  911.     /*
  912.      * If no next element, see if we need to jump the cell wall.
  913.      */
  914.     Bool success=FALSE;
  915.     while(!success)
  916.     {
  917.         LO_Element* next = eptr->lo_any.next;
  918.         last_id = eptr->lo_any.ele_id;
  919.         cell_parent = NULL;
  920.         while ( eptr && ! next  )
  921.         {
  922.             LO_Element* oldEptr = eptr;
  923.             eptr = lo_JumpCellWall(context, state, eptr);
  924.             if ( ! eptr || eptr == oldEptr )
  925.                 break; /* Ran off the end of the document */
  926.             next = eptr->lo_any.next;
  927.         }
  928.         eptr = next;
  929.         /*
  930.          * When we walk onto a cell,
  931.          * we need to walk into it if
  932.          * it isn't empty.
  933.          */
  934.         if ((eptr != NULL)&&
  935.             (eptr->type == LO_CELL)&&
  936.             (eptr->lo_cell.cell_list != NULL))
  937.         {
  938.             cell_parent = eptr;
  939.             eptr = eptr->lo_cell.cell_list;
  940.         }
  941.  
  942.         /*
  943.          * Heuristic:  For some difficult tables, you may come back
  944.          * to the same cell you left instead of progressing.
  945.          * If so, try manually moving the cell forward.
  946.          */
  947.         if ((eptr != NULL)&&(eptr->lo_any.ele_id == last_id)&&
  948.             (cell_parent != NULL))
  949.         {
  950.             LO_Element *guess;
  951.  
  952.             guess = cell_parent->lo_any.next;
  953.             /*
  954.              * If our guessed next element after the parent cell is
  955.              * a non-empty cell, make the first element in that
  956.              * cell our new element.
  957.              */
  958.             if ((guess != NULL)&&(guess->type == LO_CELL)&&
  959.                 (guess->lo_cell.cell_list != NULL))
  960.             {
  961.                 eptr = guess->lo_cell.cell_list;
  962.             }
  963.         }
  964.  
  965.         /*
  966.          * We don't want infinite loops.
  967.          * If the element ID hasn't progressed, something
  968.          * serious is wrong, and we should punt.
  969.          */
  970.         if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
  971.         {
  972.     #ifdef DEBUG
  973.     XP_TRACE(("Cell Jump loop avoidance 1\n"));
  974.     #endif /* DEBUG */
  975.         return NULL;
  976.         }
  977.  
  978.         success=TRUE; /* unless we detect a LO_TEXTBLOCK */
  979.         /* check textblock until not textblock leave prev as null if you hit
  980.            null */
  981.         if (( next && eptr->type == LO_TEXTBLOCK ) || ( next && eptr->type == LO_DESCTITLE ))
  982.         {
  983.             success=FALSE;
  984.         }
  985.     
  986.     }
  987.     return eptr;
  988. }
  989.  
  990.  
  991. /*
  992. * We changed celljumping to boundary jumping to include type 19/LO_TEXTBLOCK as a type to skip past. anthonyd
  993. */
  994.  
  995. PRIVATE
  996. LO_Element*
  997. lo_BoundaryJumpingPrev(MWContext *context, lo_DocState *state, LO_Element *eptr)
  998. {
  999.     /*
  1000.      * If no previous element, see if we need to jump the cell wall.
  1001.      */
  1002.     LO_Element* prev;
  1003.     Bool success=FALSE;
  1004.     while(!success)
  1005.     {
  1006.         prev = eptr->lo_any.prev;
  1007.         while ( eptr && ! prev  )
  1008.         {
  1009.             LO_Element* oldEptr = eptr;
  1010.             eptr = lo_JumpCellWall(context, state, eptr);
  1011.             if ( ! eptr || eptr == oldEptr )
  1012.                 break; /* Ran off the front of the document */
  1013.             prev = eptr->lo_any.prev;
  1014.             if ( ! prev )
  1015.                 continue; /* Ran off the front of the cell/document */
  1016.             if ( prev->type == LO_TABLE )
  1017.             {
  1018.                 /* That was the first cell of the table. */
  1019.                 eptr = prev;
  1020.                 prev = eptr->lo_any.prev;
  1021.                 /* Repeat the check that was made above since we've moved eptr. */
  1022.                 if ( eptr == oldEptr )
  1023.                     break; 
  1024.             }
  1025.         }
  1026.         success=TRUE; /* successfull until textblock check */
  1027.         /*
  1028.          * When we walk onto a cell,
  1029.          * we need to walk into it if
  1030.          * it isn't empty.
  1031.          */
  1032.         if ((prev != NULL)&&
  1033.             (prev->type == LO_CELL)&&
  1034.             (prev->lo_cell.cell_list_end != NULL))
  1035.         {
  1036.             prev = prev->lo_cell.cell_list_end;
  1037.         }
  1038.  
  1039.         eptr = prev;
  1040.  
  1041.         /* check textblock until not textblock leave prev as null if you hit
  1042.            null */
  1043.         if (( prev && eptr->type == LO_TEXTBLOCK ) || ( prev && eptr->type == LO_DESCTITLE ))
  1044.         {
  1045.             success=FALSE;
  1046.         }
  1047.     }
  1048.     return eptr;
  1049. }
  1050.  
  1051.  
  1052.  
  1053. static void
  1054. lo_bump_position(MWContext *context, lo_DocState *state,
  1055.         LO_Element *sel, int32 pos,
  1056.         LO_Element **new_sel, int32 *new_pos, Bool forward)
  1057. {
  1058.     LO_Element *eptr;
  1059.     int32 position;
  1060.  
  1061.     eptr = sel;
  1062.     position = pos;
  1063.  
  1064.     /*
  1065.      * Moving forward one selection position
  1066.      */
  1067.     if (forward != FALSE)
  1068.     {
  1069.         /*
  1070.          * If it is not a text element, just move
  1071.          * to the next element.
  1072.          */
  1073.         if (eptr->lo_any.type != LO_TEXT)
  1074.         {
  1075.             eptr = lo_BoundaryJumpingNext(context, state, eptr);
  1076.             if (eptr == NULL)
  1077.             {
  1078.                 eptr = sel;
  1079.             }
  1080.             /*
  1081.              * Else start at the beginning of the next element
  1082.              */
  1083.             else
  1084.             {
  1085.                 position = 0;
  1086.             }
  1087.         }
  1088.         /*
  1089.          * Else this is a text element, so check moving
  1090.          * forward within the element.
  1091.          */
  1092.         else
  1093.         {
  1094.             intn maxPosition = lo_GetLastCharBeginPosition(eptr);
  1095.             /*
  1096.              * If we are already at the end of the text
  1097.              * element we are back to moving on to the
  1098.              * next element.
  1099.              */
  1100.             if (position < maxPosition)
  1101.             {
  1102.                  position = lo_IncrementPosition(eptr, position);
  1103.             }
  1104.             else
  1105.             {
  1106.                 eptr = lo_BoundaryJumpingNext(context, state, eptr);
  1107.                 if (eptr == NULL)
  1108.                 {
  1109.                     eptr = sel;
  1110.                 }
  1111.                 /*
  1112.                  * Else start at the beginning of the next element
  1113.                  */
  1114.                 else
  1115.                 {
  1116.                     position = 0;
  1117.                 }
  1118.             }
  1119.         }
  1120.     }
  1121.     /*
  1122.      * Else moving backward one selection position
  1123.      */
  1124.     else
  1125.     {
  1126.         /*
  1127.          * If it is not a text element, just move
  1128.          * to the previous element.
  1129.          */
  1130.         if (eptr->lo_any.type != LO_TEXT)
  1131.         {
  1132.             eptr = lo_BoundaryJumpingPrev(context, state, eptr);
  1133.  
  1134.             /*
  1135.              * If no previous element, don't move it.
  1136.              */
  1137.             if (eptr == NULL)
  1138.             {
  1139.                 eptr = sel;
  1140.             }
  1141.             /*
  1142.              * Else start at the end of the previous element.
  1143.              * (only matters for text elements)
  1144.              */
  1145.             else
  1146.             {
  1147.                 position = lo_GetLastCharBeginPosition(eptr);
  1148.             }
  1149.         }
  1150.         /*
  1151.          * Else this is a text element, so check moving
  1152.          * backward within the element.
  1153.          */
  1154.         else
  1155.         {
  1156.             /*
  1157.              * If we are already at the beginning of the text
  1158.              * element we are back to moving back to the
  1159.              * previous element.
  1160.              */
  1161.             if (position == 0)
  1162.             {
  1163.                 eptr = lo_BoundaryJumpingPrev(context, state, eptr);
  1164.  
  1165.                 /*
  1166.                  * If no previous element, don't move it.
  1167.                  */
  1168.                 if (eptr == NULL)
  1169.                 {
  1170.                     eptr = sel;
  1171.                 }
  1172.                 /*
  1173.                  * Else start at the end of the previous
  1174.                  * element.
  1175.                  * (only matters for text elements)
  1176.                  */
  1177.                 else
  1178.                 {
  1179.                     position = lo_GetLastCharBeginPosition(eptr);
  1180.                 }
  1181.             }
  1182.             /*
  1183.              * This is the easy case, move one position
  1184.              * backward in the selected text element.
  1185.              */
  1186.             else
  1187.             {
  1188.                 position = lo_DecrementPosition(eptr, position);
  1189.             }
  1190.         }
  1191.     }
  1192.  
  1193.     *new_sel = eptr;
  1194.     *new_pos = position;
  1195. }
  1196.  
  1197. PRIVATE
  1198. Bool
  1199. lo_BumpPosition(MWContext *context, lo_DocState *state, LO_Position* position, Bool forward)
  1200. {
  1201.     LO_Element* newElement;
  1202.     int32 newPosition;
  1203.     lo_bump_position(context, state, position->element, position->position,
  1204.         &newElement, &newPosition, forward);
  1205.     if ( newElement == NULL
  1206.             || lo_compare_selections(newElement, newPosition, position->element, position->position) == 0)
  1207.     {
  1208.         /* We ran out of room. */
  1209.         return FALSE;
  1210.     }
  1211.     position->element = newElement;
  1212.     position->position = newPosition;
  1213.     return TRUE;
  1214. }
  1215.  
  1216. PRIVATE
  1217. Bool lo_ValidEditableElement(MWContext *context, LO_Element* eptr)
  1218. {
  1219.     return lo_EditableElement(eptr->type) &&
  1220.         ( (!EDT_IS_EDITOR(context) ) ||
  1221.             (eptr->lo_any.edit_element != 0 &&
  1222.              eptr->lo_any.edit_offset >= 0));
  1223. }
  1224.  
  1225. PRIVATE
  1226. Bool lo_ValidEditableElementIncludingParagraphMarks(MWContext *context, LO_Element* pElement)
  1227. {
  1228.     return lo_ValidEditableElement(context, pElement)
  1229.         || (pElement->type== LO_LINEFEED
  1230.             && pElement->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH);
  1231. }
  1232.  
  1233. /* Moves by one editable position forward or backward. Returns FALSE if it couldn't.
  1234.  * Assumes that position is normalized, doesn't denormalize it.
  1235.  */
  1236.  
  1237. PRIVATE
  1238. Bool
  1239. lo_BumpNormalizedEditablePosition(MWContext *context, lo_DocState *state,
  1240.     LO_Element **pEptr, int32 *pPosition, Bool direction)
  1241. {
  1242.     LO_Element* newEptr;
  1243.     int32 newPosition;
  1244.     if ( ! ( pEptr && *pEptr && pPosition ) )
  1245.     {
  1246.         XP_ASSERT(FALSE);
  1247.         return FALSE;
  1248.     }
  1249.  
  1250.     newEptr = *pEptr;
  1251.     newPosition = *pPosition;
  1252.  
  1253.     {
  1254.         int32 length = lo_GetElementLength(newEptr);
  1255.         if ( ! (newPosition >= 0 && ( newPosition < length || length == 0 ) ) )
  1256.         {
  1257.             XP_ASSERT(FALSE);
  1258.             return FALSE;
  1259.         }
  1260.     }
  1261.  
  1262.     do {
  1263.         LO_Element* oldEptr = newEptr;
  1264.         int32 oldPosition = newPosition;
  1265.         lo_bump_position(context, state, newEptr, newPosition,
  1266.             &newEptr, &newPosition, direction);
  1267.         if ( newEptr == NULL
  1268.             || lo_compare_selections(newEptr, newPosition, oldEptr, oldPosition) == 0)
  1269.         {
  1270.             /* We ran out of room. */
  1271.             return FALSE;
  1272.         }
  1273.     } while ( ! lo_ValidEditableElementIncludingParagraphMarks(context, newEptr) );
  1274.     *pEptr = newEptr;
  1275.     *pPosition = newPosition;
  1276.     return TRUE;
  1277. }
  1278.  
  1279. /* This function is only intended for the internal use of
  1280.  * lo_BumpEditablePosition. It handles the quick case where
  1281.  * the position doesn't cross over the object boundary.
  1282.  */
  1283.  
  1284. PRIVATE
  1285. Bool
  1286. lo_QuickBumpEditablePosition(MWContext *context, lo_DocState *state,
  1287.     LO_Element **pEptr, int32 *pPosition, Bool direction)
  1288. {
  1289.     if ( direction )
  1290.     {
  1291.         if ( *pPosition < lo_GetMaximumInsertPointPosition(*pEptr) )
  1292.         {
  1293.             /* Easy case: Moving forward within an element. */
  1294.             *pPosition = lo_IncrementPosition(*pEptr, *pPosition);
  1295.  
  1296. /*            XP_TRACE(("new position = %d", *pPosition)); */
  1297.             return TRUE;
  1298.         }
  1299.     }
  1300.     else
  1301.     {
  1302.         if ( *pPosition > 0 )
  1303.         {
  1304.             /* Easy case: Moving backward within an element. */
  1305.             *pPosition = lo_DecrementPosition(*pEptr, *pPosition);
  1306.  
  1307. /*            XP_TRACE(("new position = %d", *pPosition)); */
  1308.             return TRUE;
  1309.         }
  1310.     }
  1311.     return FALSE;
  1312. }
  1313.  
  1314. /* Moves by one editable position forward or backward. Returns FALSE if it couldn't.
  1315.  * position can be denormalized on entry and may be denormalized on exit.
  1316.  */
  1317.  
  1318. Bool
  1319. lo_BumpEditablePosition(MWContext *context, lo_DocState *state,
  1320.     LO_Element **pEptr, int32 *pPosition, Bool direction)
  1321. {
  1322.     LO_Element* newEptr;
  1323.     int32 newPosition;
  1324.     if ( ! ( pEptr && *pEptr && pPosition ) )
  1325.     {
  1326.         XP_ASSERT(FALSE);
  1327.         return FALSE;
  1328.     }
  1329.     newEptr = *pEptr;
  1330.     newPosition = *pPosition;
  1331.  
  1332.     if ( ! lo_QuickBumpEditablePosition(context, state, &newEptr, &newPosition, direction ) )
  1333.     {
  1334.         /* Normalize, and try again. */
  1335.         if ( ! lo_NormalizeSelectionPoint(context, state, &newEptr, &newPosition) )
  1336.             return FALSE;
  1337.  
  1338.         if ( ! lo_QuickBumpEditablePosition(context, state, &newEptr, &newPosition, direction ) )
  1339.         {
  1340.             /* Now that it's normalized, try the heavy-duty bump. */
  1341.             if ( ! lo_BumpNormalizedEditablePosition(context, state, &newEptr, &newPosition, direction) )
  1342.             {
  1343.                 return FALSE;
  1344.             }
  1345.         }
  1346.     }
  1347.  
  1348.     *pEptr = newEptr;
  1349.     *pPosition = newPosition;
  1350.     return TRUE;
  1351. }
  1352.  
  1353. PRIVATE
  1354. Bool
  1355. lo_BumpEditablePositionForward(MWContext *context, lo_DocState *state,
  1356.     LO_Element **pEptr, int32 *pPosition)
  1357. {
  1358.     return lo_BumpEditablePosition(context, state, pEptr, pPosition, TRUE);
  1359. }
  1360.  
  1361.  
  1362. PRIVATE
  1363. Bool
  1364. lo_BumpEditablePositionBackward(MWContext *context, lo_DocState *state,
  1365.     LO_Element **pEptr, int32 *pPosition)
  1366. {
  1367.     return lo_BumpEditablePosition(context, state, pEptr, pPosition, FALSE);
  1368. }
  1369.  
  1370. PRIVATE
  1371. void
  1372. lo_ChangeSelection(MWContext *context, lo_DocState *state,
  1373.     LO_Element *changed_start, LO_Element *changed_end,
  1374.     int32 changed_start_pos, int32 changed_end_pos)
  1375. {
  1376.     LO_Element *eptr;
  1377.     int32 position;
  1378.     LO_Element *start, *end;
  1379.     int32 start_pos, end_pos;
  1380.     intn compare_start;
  1381.     intn compare_end;
  1382.  
  1383.     /*
  1384.      * Handle case where there's no existing selection
  1385.      */
  1386.  
  1387.     if ( state->selection_start == NULL )
  1388.     {
  1389.         lo_HighlightSelect(context, state,
  1390.             changed_start, changed_start_pos,
  1391.             changed_end, changed_end_pos, TRUE);
  1392.         return;
  1393.     }
  1394.  
  1395.     start = state->selection_start;
  1396.     start_pos = state->selection_start_pos;
  1397.     end = state->selection_end;
  1398.     end_pos = state->selection_end_pos;
  1399.     lo_NormalizeSelectionEnd(context, state, &end, &end_pos);
  1400.  
  1401.     /*
  1402.      * If the end of the old selection is less than
  1403.      * the start of the new one.  Or the end of the new one is
  1404.      * less than the start of the old one.
  1405.      * Erase the old one, draw the new one.
  1406.      */
  1407.     if ((lo_compare_selections(end, end_pos, changed_start,
  1408.         changed_start_pos) < 0)||
  1409.         (lo_compare_selections(changed_end, changed_end_pos, start,
  1410.         start_pos) < 0))
  1411.     {
  1412.         lo_HighlightSelect(context, state,
  1413.             start, start_pos, end, end_pos, FALSE);
  1414.         lo_HighlightSelect(context, state,
  1415.             changed_start, changed_start_pos,
  1416.             changed_end, changed_end_pos, TRUE);
  1417.     }
  1418.     else
  1419.     {
  1420.         compare_start = lo_compare_selections(changed_start,
  1421.             changed_start_pos, start, start_pos);
  1422.         compare_end = lo_compare_selections(changed_end,
  1423.             changed_end_pos, end, end_pos);
  1424.         /*
  1425.          * If the start position has moved either draw more,
  1426.          * or erase some of the old.
  1427.          */
  1428.         if (compare_start < 0)
  1429.         {
  1430.             lo_HighlightSelect(context, state,
  1431.                 changed_start, changed_start_pos,
  1432.                 start, start_pos, TRUE);
  1433.         }
  1434.         else if (compare_start > 0)
  1435.         {
  1436.             lo_bump_position(context, state,
  1437.                 changed_start, changed_start_pos,
  1438.                 &eptr, &position, FALSE);
  1439.             lo_HighlightSelect(context, state,
  1440.                 start, start_pos, eptr, position, FALSE);
  1441.         }
  1442.  
  1443.         /*
  1444.          * If the end position has moved either draw more,
  1445.          * or erase some of the old.
  1446.          */
  1447.         if (compare_end > 0)
  1448.         {
  1449.             lo_HighlightSelect(context, state, end, end_pos,
  1450.                 changed_end, changed_end_pos, TRUE);
  1451.         }
  1452.         else if (compare_end < 0)
  1453.         {
  1454.             lo_bump_position(context, state,
  1455.                 changed_end, changed_end_pos,
  1456.                 &eptr, &position, TRUE);
  1457.             lo_HighlightSelect(context, state,
  1458.                 eptr, position, end, end_pos, FALSE);
  1459.         }
  1460.     }
  1461. }
  1462.  
  1463. Bool LO_SelectElement( MWContext *context, LO_Element *eptr, 
  1464.     int32 position, Bool bFromStart  )
  1465. {
  1466.     int32 doc_id;
  1467.     lo_TopState *top_state;
  1468.     lo_DocState *state;
  1469.  
  1470.     /*
  1471.      * Get the unique document ID, and retreive this
  1472.      * documents layout state.
  1473.      */
  1474.     doc_id = XP_DOCID(context);
  1475.     top_state = lo_FetchTopState(doc_id);
  1476.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1477.     {
  1478.         return FALSE;
  1479.     }
  1480.     state = top_state->doc_state;
  1481.  
  1482.     /* If we've been handed a non-editable item, go to the previous editable item */
  1483.     if ( ! lo_EnsureEditableSearchPrev2(context, state, &eptr, &position) )
  1484.     {
  1485.         return FALSE;
  1486.     }
  1487.  
  1488.     if ( lo_IsEdgeOfDocument2(context, state, eptr, position, ! bFromStart ) )
  1489.     {
  1490.         return FALSE;
  1491.     }
  1492.  
  1493.     /* If we're selecting backwards, actually select one lower */
  1494.     if ( bFromStart )
  1495.     {
  1496.         if ( ! lo_BumpEditablePositionBackward(context, state,
  1497.             &eptr, &position) ) return FALSE;
  1498.     }
  1499.     /* End is the same as the beginning. */
  1500.     lo_FullSetSelection(context, state, eptr, position, eptr, position, bFromStart);
  1501.     return TRUE;
  1502. }
  1503.  
  1504. /* Internal routine that should not be exported */
  1505. void LO_ExtendSelectionFromElement( MWContext *context, LO_Element *eptr, 
  1506.     int32 position, Bool bFromStart  )
  1507. {
  1508.     int32 doc_id;
  1509.     lo_TopState *top_state;
  1510.     lo_DocState *state;
  1511.     LO_Element *changed_start, *changed_end;
  1512.     int32 changed_start_pos, changed_end_pos;
  1513.  
  1514.     /*
  1515.      * Get the unique document ID, and retreive this
  1516.      * documents layout state.
  1517.      */
  1518.     doc_id = XP_DOCID(context);
  1519.     top_state = lo_FetchTopState(doc_id);
  1520.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1521.     {
  1522.         return;
  1523.     }
  1524.     state = top_state->doc_state;
  1525.  
  1526.     /*
  1527.      * If we have started a new selection, replace the old one with
  1528.      * this new one before extending
  1529.      */
  1530.     if (state->selection_new != NULL)
  1531.     {
  1532.         lo_StartNewSelection(context, state, eptr, position);
  1533.         return;
  1534.     }
  1535.  
  1536.     /*
  1537.      * If we somehow got here without having any selection or started
  1538.      * selection, start it now where we are.
  1539.      */
  1540.     if (state->selection_start == NULL)
  1541.     {
  1542.         state->selection_start = eptr;
  1543.         state->selection_start_pos = position;
  1544.         state->selection_end = state->selection_start;
  1545.         state->selection_end_pos = state->selection_start_pos;
  1546.         state->extending_start = FALSE;
  1547.     }
  1548.  
  1549.     /*
  1550.      * By some horrendous bug the start was set and the end was not,
  1551.      * and there was no newly started selection.  Correct this now.
  1552.      */
  1553.     if (state->selection_end == NULL)
  1554.     {
  1555.         state->selection_end = state->selection_start;
  1556.         state->selection_end_pos = state->selection_start_pos;
  1557.         state->extending_start = FALSE;
  1558.     }
  1559.  
  1560.     /*
  1561.      * Extend the same side we extended last time.
  1562.      */
  1563.     if (state->extending_start != FALSE)
  1564.     {
  1565.         changed_start = eptr;
  1566.         changed_start_pos = position;
  1567.         changed_end = state->selection_end;
  1568.         changed_end_pos = state->selection_end_pos;
  1569.     }
  1570.     else
  1571.     {
  1572.         changed_start = state->selection_start;
  1573.         changed_start_pos = state->selection_start_pos;
  1574.         changed_end = eptr;
  1575.         changed_end_pos = position;
  1576.     }
  1577.  
  1578.     /*
  1579.      * If we crossed our start and end positions, switch
  1580.      * them, and switch which end we are extending from.
  1581.      */
  1582.     if (changed_start->lo_any.ele_id > changed_end->lo_any.ele_id)
  1583.     {
  1584.         eptr = changed_start;
  1585.         position = changed_start_pos;
  1586.         changed_start = changed_end;
  1587.         changed_start_pos = changed_end_pos;
  1588.         changed_end = eptr;
  1589.         changed_end_pos = position;
  1590.         if (state->extending_start != FALSE)
  1591.         {
  1592.             state->extending_start = FALSE;
  1593.         }
  1594.         else
  1595.         {
  1596.             state->extending_start = TRUE;
  1597.         }
  1598.     }
  1599.     else if ((changed_start->lo_any.ele_id == changed_end->lo_any.ele_id)&&
  1600.          (changed_start_pos > changed_end_pos))
  1601.     {
  1602.         position = changed_start_pos;
  1603.         changed_start_pos = changed_end_pos;
  1604.         changed_end_pos = position;
  1605.         if (state->extending_start != FALSE)
  1606.         {
  1607.             state->extending_start = FALSE;
  1608.         }
  1609.         else
  1610.         {
  1611.             state->extending_start = TRUE;
  1612.         }
  1613.     }
  1614.  
  1615.     lo_ChangeSelection(context, state, changed_start, changed_end,
  1616.         changed_start_pos, changed_end_pos);
  1617.  
  1618.     lo_SetSelect(context, state, changed_start, changed_start_pos,
  1619.         changed_end, changed_end_pos, TRUE);
  1620.  
  1621.     state->selection_start = changed_start;
  1622.     state->selection_start_pos = changed_start_pos;
  1623.     state->selection_end = changed_end;
  1624.     state->selection_end_pos = changed_end_pos;
  1625.  
  1626.     /*
  1627.      * Cannot have a new selection after an extend.
  1628.      */
  1629.     state->selection_new = NULL;
  1630.     state->selection_new_pos = 0;
  1631.  
  1632. #if 0
  1633.     /*
  1634.      * If the beginning and the endpoints were not the beginning and end points
  1635.      *  then the editors notion of bFromStart is backwards.
  1636.     */
  1637.     if( state->extending_start ){
  1638.         state->extending_start = !bFromStart;
  1639.     }
  1640.     else {
  1641.         state->extending_start = bFromStart;
  1642.     }
  1643. #endif
  1644. }
  1645.  
  1646. void LO_SelectRegion( MWContext *context, LO_Element *begin, int32 beginPosition, 
  1647.     LO_Element* end, int32 endPosition, Bool fromStart , Bool forward)
  1648. {
  1649.     int32 doc_id;
  1650.     lo_TopState *top_state;
  1651.     lo_DocState *state;
  1652.     /*
  1653.      * Get the unique document ID, and retreive this
  1654.      * documents layout state.
  1655.      */
  1656.     doc_id = XP_DOCID(context);
  1657.     top_state = lo_FetchTopState(doc_id);
  1658.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1659.     {
  1660.         return;
  1661.     }
  1662.     state = top_state->doc_state;
  1663.  
  1664.     if ( ! begin || !end ) {
  1665.         XP_ASSERT(FALSE);
  1666.         return;
  1667.     }
  1668.  
  1669. #if 0
  1670.     lo_NormalizeSelectionPoint(context, state, &begin, &beginPosition);
  1671.     lo_NormalizeSelectionPoint(context, state, &end, &endPosition);
  1672. #endif
  1673.  
  1674. #if 0
  1675.     XP_TRACE(("begin %d %d %d end %d %d %d start %d forward %d",
  1676.         begin->type, begin->lo_any.ele_id, beginPosition,
  1677.         end->type, end->lo_any.ele_id, endPosition,
  1678.         fromStart, forward));
  1679. #endif
  1680.     /* Normalize end-points for end-of line conditions */
  1681.     if ( fromStart )
  1682.     {
  1683.         if ( !forward && begin->type == LO_LINEFEED
  1684.             && beginPosition == 0 )
  1685.         {
  1686.             lo_BumpEditablePositionBackward(context, state, &begin, &beginPosition);
  1687.         }
  1688.     }
  1689.     else
  1690.     {
  1691.         LO_Element* prev;
  1692.         if ( forward && endPosition == 0 &&
  1693.             ( end->type == LO_LINEFEED ||
  1694.                 ((prev = lo_BoundaryJumpingPrev(context, state, end)) != NULL && prev->type == LO_LINEFEED ) ) )
  1695.         {
  1696.             lo_BumpEditablePositionForward(context, state, &end, &endPosition);
  1697.         }
  1698.     }
  1699.     LO_StartSelectionFromElement( context, begin, beginPosition, NULL );
  1700.     LO_ExtendSelectionFromElement( context, end, endPosition, fromStart );
  1701.     state->extending_start = fromStart;
  1702. }
  1703.  
  1704. void
  1705. lo_SetSelection(MWContext *context, LO_Selection* selection, Bool extendingStart)
  1706. {
  1707.     int32 doc_id;
  1708.     lo_TopState *top_state;
  1709.     lo_DocState *state;
  1710.     /*
  1711.      * Get the unique document ID, and retreive this
  1712.      * documents layout state.
  1713.      */
  1714.     doc_id = XP_DOCID(context);
  1715.     top_state = lo_FetchTopState(doc_id);
  1716.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  1717.     {
  1718.         return;
  1719.     }
  1720.     state = top_state->doc_state;
  1721.  
  1722.     lo_FullSetSelection( context, state,
  1723.         selection->begin.element,
  1724.         selection->begin.position,
  1725.         selection->end.element,
  1726.         selection->end.position, extendingStart);
  1727. }
  1728.  
  1729. void
  1730. lo_FullSetSelection(MWContext *context, lo_DocState * state,
  1731.     LO_Element* start, int32 start_pos,
  1732.     LO_Element* end, int32 end_pos,
  1733.     Bool extendFromStart)
  1734. {
  1735.     LO_Element* normalizedStart = start;
  1736.     int32 normalizedStartPosition = start_pos;
  1737.     LO_Element* normalizedEnd = end;
  1738.     int32 normalizedEndPosition = end_pos;
  1739.  
  1740.     LO_ASSERT_POSITION2(context, start, start_pos);
  1741.     LO_ASSERT_POSITION2(context, end, end_pos);
  1742.  
  1743.     /* The non-editor portions of layout can't deal with a
  1744.      * de-normalized positions. So we normalize it here.
  1745.      */
  1746.  
  1747.     /* LO_DUMP_SELECTION2("FullSetSelection", start, start_pos, end, end_pos); */
  1748.     #if 0
  1749.         lo_NormalizeSelectionPoint(context, state, &normalizedStart, &normalizedStartPosition);
  1750.     #endif
  1751.  
  1752.     if ( lo_GetElementLength(normalizedStart) <= normalizedStartPosition )
  1753.     {
  1754.         normalizedStart = lo_BoundaryJumpingNext(context, state, normalizedStart);
  1755.         normalizedStartPosition = 0;
  1756.     }
  1757.     lo_NormalizeSelectionEnd(context, state, &normalizedEnd, &normalizedEndPosition);
  1758.  
  1759.     /* If we've been handed a bad selection, just return. */
  1760.     if ( ! ( normalizedStart && normalizedEnd &&
  1761.         lo_compare_selections(normalizedStart, normalizedStartPosition,
  1762.             normalizedEnd, normalizedEndPosition) <= 0 ) )
  1763.     {
  1764.         XP_TRACE(("lo_FullSetSelection: bad selection."));
  1765. #ifdef DEBUG_EDIT_LAYOUT
  1766.         XP_ASSERT(FALSE);
  1767. #endif
  1768.         return;
  1769.     }
  1770.  
  1771.     lo_ChangeSelection(context, state, normalizedStart, normalizedEnd,
  1772.         normalizedStartPosition, normalizedEndPosition);
  1773.  
  1774.     lo_SetSelect(context, state, normalizedStart, normalizedStartPosition,
  1775.         normalizedEnd, normalizedEndPosition, TRUE);
  1776.  
  1777.     state->selection_start = start;
  1778.     state->selection_start_pos = start_pos;
  1779.     state->selection_end = end;
  1780.     state->selection_end_pos = end_pos;
  1781.     state->extending_start = extendFromStart;
  1782.  
  1783.     /*
  1784.      * Cannot have a new selection after an extend.
  1785.      */
  1786.     state->selection_new = NULL;
  1787.     state->selection_new_pos = 0;
  1788. }
  1789.  
  1790. /* Get the selection edges, expressed as insert points. */
  1791.  
  1792. PRIVATE
  1793. void
  1794. lo_GetSelectionEdge(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition, Bool bForward)
  1795. {
  1796.     if(state->selection_start && state->selection_end )
  1797.     {
  1798.         if ( !bForward )
  1799.         {
  1800.             *pElement = state->selection_start;
  1801.             *pPosition = state->selection_start_pos;
  1802.         }
  1803.         else
  1804.         {
  1805.             *pElement = state->selection_end;
  1806.             *pPosition = state->selection_end_pos;
  1807.             lo_ConvertSelectionEndToInsertPoint(context, state, pElement, pPosition);
  1808.         }
  1809.     }
  1810.     else
  1811.     {
  1812.         /* Use insert point */
  1813.         *pElement = state->selection_new;
  1814.         *pPosition = state->selection_new_pos;
  1815.     }
  1816.  
  1817. }
  1818.  
  1819. void
  1820. lo_GetAnchorPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition)
  1821. {
  1822.     lo_GetSelectionEdge(context, state, pElement, pPosition, state->extending_start);
  1823. }
  1824. void
  1825. lo_GetExtensionPoint(MWContext *context, lo_DocState * state, LO_Element** pElement, int32* pPosition)
  1826. {
  1827.     lo_GetSelectionEdge(context, state, pElement, pPosition, !state->extending_start);
  1828. }
  1829.  
  1830. Bool
  1831. lo_IsEndOfParagraph2(MWContext *context, LO_Element* element, int32 position)
  1832. {
  1833.     if ( ! element )
  1834.     {
  1835.         XP_ASSERT( FALSE );
  1836.         return FALSE;
  1837.     }
  1838.  
  1839.     /* The browser doesn't set break_type, so every line is a paragraph to it. */
  1840.     if ( element->type == LO_LINEFEED && 
  1841.         ( ! EDT_IS_EDITOR(context)
  1842.             || element->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH)) {
  1843.         return TRUE;
  1844.     }
  1845.     return FALSE;
  1846. }
  1847.  
  1848. PRIVATE
  1849. Bool
  1850. lo_IsEndOfParagraph(MWContext *context, LO_Position* position)
  1851. {
  1852.     XP_ASSERT( position );
  1853.     return lo_IsEndOfParagraph2(context, position->element, position->position);
  1854. }
  1855.  
  1856. PRIVATE
  1857. Bool
  1858. lo_IsHardBreak2(MWContext *context, LO_Element* element, int32 position)
  1859. {
  1860.     if ( ! element )
  1861.     {
  1862.         XP_ASSERT( FALSE );
  1863.         return FALSE;
  1864.     }
  1865.  
  1866.     /* The browser doesn't set break_type, so every line is a hard break to it. */
  1867.     if ( element->type == LO_LINEFEED && 
  1868.         ( ! EDT_IS_EDITOR(context)
  1869.             || element->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT)) {
  1870.         return TRUE;
  1871.     }
  1872.     return FALSE;
  1873. }
  1874.  
  1875. PRIVATE
  1876. Bool
  1877. lo_ExtendToIncludeHardBreak(MWContext* context, lo_DocState *state, LO_Selection* selection)
  1878. {
  1879.     /* If this is a selection that ends in a hard break, extend the
  1880.      * selection to include the hard break.
  1881.      */
  1882.     Bool result = FALSE;
  1883.     if ( EDT_IS_EDITOR(context) )
  1884.     {
  1885.         LO_Position* end = &selection->end;
  1886.         if ( lo_IsHardBreak2(context, end->element,0) )
  1887.         {
  1888.             result = TRUE; /* Already at a break character. */
  1889.         }
  1890.         else
  1891.         {
  1892.             if ( end->position >= lo_GetLastCharEndPosition(end->element) )
  1893.             {
  1894.                 LO_Element* eol = lo_BoundaryJumpingNext(context, state, end->element);
  1895.                 if ( lo_IsHardBreak2(context, eol,0) )
  1896.                 {
  1897.                     /* Extend to include the paragraph end element */
  1898.                     end->element = eol;
  1899.                     end->position = 0;
  1900.                     result = TRUE;
  1901.                 }
  1902.             }
  1903.         }
  1904.     }
  1905.     return result;
  1906. }
  1907.  
  1908. #ifdef DEBUG
  1909. void
  1910. lo_ValidatePosition(MWContext *context, LO_Position* position)
  1911. {
  1912.     XP_ASSERT( position );
  1913.     if ( position ) {
  1914.         lo_ValidatePosition2(context, position->element, position->position);
  1915.     }
  1916. }
  1917.  
  1918. void
  1919. lo_ValidatePosition2(MWContext *context, LO_Element* element, int32 position)
  1920. {
  1921.     int32 length;
  1922.     XP_ASSERT( element );
  1923.     XP_ASSERT( context );
  1924.     if ( context && element )
  1925.     {
  1926.         length = lo_GetElementLength(element);
  1927.         XP_ASSERT( 0 <= position );
  1928.         XP_ASSERT( position <= length );
  1929.         if ( EDT_IS_EDITOR(context) )
  1930.         {
  1931.             /* Either it's a line-feed with an eop, or it has an edit_element. */
  1932.             if ( ! element->lo_any.edit_element )
  1933.             {
  1934.                 if (element->type == LO_LINEFEED)
  1935.                 {
  1936.                     if ( ! lo_IsEndOfParagraph2(context, element, position) )
  1937.                     {
  1938.                         XP_TRACE(("lo_ValidatePosition2: illegal position: a line feed without an end-of-paragraph marker."));
  1939. #ifdef DEBUG_EDIT_LAYOUT
  1940.                             XP_ASSERT(FALSE);
  1941. #endif
  1942.                     }
  1943.                 }
  1944.                 else
  1945.                 {
  1946. #ifdef DEBUG_EDIT_LAYOUT
  1947.                     XP_ASSERT(FALSE);
  1948. #endif
  1949.                 }
  1950.             }
  1951.         }
  1952.     }
  1953. }
  1954.  
  1955. void
  1956. lo_ValidateSelection(MWContext *context, LO_Selection* selection)
  1957. {
  1958.     XP_ASSERT( selection );
  1959.     lo_ValidateSelection2( context, selection->begin.element, selection->begin.position,
  1960.         selection->end.element, selection->end.position );
  1961. }
  1962.  
  1963. void
  1964. lo_ValidateSelection2(MWContext *context, LO_Element* beginElement, int32 beginPosition, LO_Element* endElement, int32 endPosition)
  1965. {
  1966.     lo_ValidatePosition2( context, beginElement, beginPosition );
  1967.     lo_ValidatePosition2( context, endElement, endPosition );
  1968.     XP_ASSERT( lo_compare_selections(beginElement, beginPosition,
  1969.         endElement, endPosition) <= 0 );
  1970. }
  1971.  
  1972. #endif
  1973.  
  1974. /*
  1975.  * An insert point is one edit position greater than a selection end. But there are
  1976.  * two insert points for the edit position that falls between elements. One
  1977.  * insert point is for the end of the previous element. The other is for the
  1978.  * beginning of the next element. There are two corresponding selection ends.
  1979.  *
  1980.  *  end of previous element:
  1981.  *    insertion point: element = <previous>, position = <previous.length>
  1982.  *    selection end:   element = <previous>, position = <previous.length> - 1
  1983.  *
  1984.  *  start of next element:
  1985.  *    insertion point: element = <next>,     position = 0
  1986.  *    selection end:   element = <previous>, position = <previous.length>
  1987.  */
  1988.  
  1989. PRIVATE
  1990. Bool lo_IsAtStartOfLine(MWContext* context, lo_DocState* state,
  1991.     LO_Element* element, int32 position)
  1992. {
  1993.     LO_Element* prev;
  1994.     if ( position != 0 || ! element )
  1995.         return FALSE;
  1996.     prev = element->lo_any.prev;
  1997.     if ( ! prev )
  1998.         return TRUE;
  1999.     if ( prev->type == LO_LINEFEED ||
  2000.         prev->type == LO_BULLET )
  2001.         return TRUE;
  2002.     /* Numbered list test. */
  2003.     if ( EDT_IS_EDITOR(context) && prev->type == LO_TEXT && prev->lo_text.edit_element == 0 )
  2004.         return TRUE;
  2005.     return FALSE;
  2006. }
  2007.  
  2008. Bool lo_ConvertInsertPointToSelectionEnd(MWContext* context, lo_DocState * state,
  2009.     LO_Element** pElement, int32* pPosition)
  2010. {
  2011.     LO_Element* eptr = *pElement;
  2012.     int32 position = *pPosition;
  2013.  
  2014.     /* Special case for insert points at beginning of lines. */
  2015.     if ( lo_IsAtStartOfLine(context, state, eptr, position) )
  2016.     {
  2017.         /* Skip backwards to line feed that has end-of-paragraph set */
  2018.         while ( eptr && lo_BoundaryJumpingPrev(context, state, eptr) ) {
  2019.             lo_bump_position(context, state, eptr, position, &eptr, &position, FALSE);
  2020.             if ( ! eptr )
  2021.             {
  2022.                 break;
  2023.             }
  2024.  
  2025.             if ( eptr->type == LO_LINEFEED )
  2026.             {
  2027.                 if ( eptr->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT )
  2028.                 {
  2029.                     break;
  2030.                 }
  2031.             }
  2032.             /* Linefeeds are editable, but the if test above filters them out. */
  2033.             else if (lo_EditableElement(eptr->type) )
  2034.             {
  2035.                 break;
  2036.             }
  2037.         }
  2038.         position = lo_GetLastCharBeginPosition(eptr);
  2039.     }
  2040.     else
  2041.     if ( lo_BumpEditablePositionBackward(context, state, &eptr, &position) )
  2042.     {
  2043.         if ( *pPosition == 0 )
  2044.         {
  2045.             /* This is the "Start of next element" case.  */
  2046.             lo_BumpEditablePositionForward(context, state, &eptr, &position);
  2047.         }
  2048.     }
  2049.     else
  2050.     {
  2051.         /* If we can't go backwards, it means that the insert point was at the beginning of the document.
  2052.          * This is an error condition, because there is no legal selection end that corresponds
  2053.          * to this insert point.
  2054.          */
  2055.         return FALSE;
  2056.     }
  2057.  
  2058.     /* It turns out that the end is the address of the last byte selected, not just the position of the
  2059.      * first byte of the two-byte character. So we have to bump forward and subtract one.
  2060.      *
  2061.      * (If we're a denormalized selection end, we don't increment our position.
  2062.      *  that's what the "if" is for.)
  2063.      */
  2064.     if ( position < lo_GetElementLength(eptr) )
  2065.     {
  2066.         position = lo_IncrementPosition(eptr, position) - 1;
  2067.     }
  2068.     *pElement = eptr;
  2069.     *pPosition = position;
  2070.     return TRUE;
  2071. }
  2072.  
  2073. Bool lo_ConvertSelectionEndToInsertPoint(MWContext* context, lo_DocState * state,
  2074.     LO_Element** pElement, int32* pPosition)
  2075. {
  2076.     LO_Element* eptr = *pElement;
  2077.     int32 position = *pPosition;
  2078.     int32 length = lo_GetElementLength(*pElement);
  2079.  
  2080.     LO_ASSERT_POSITION2( context, eptr, position );
  2081.     /* It turns out that the end is the address of the last byte selected, not just the position of the
  2082.      * first byte of the two-byte character. So we have to add one and bump backward.
  2083.      */
  2084.     if ( position >= length )
  2085.     {
  2086.         /* this is the end-of-previous-element case. */
  2087.         XP_ASSERT( position == length );
  2088.         position = lo_GetLastCharBeginPosition(*pElement);
  2089.  
  2090.         /* If we can't go forward, it means that the selection end was beyond the end of the document.
  2091.          * This is an error condition.
  2092.          */
  2093.         if ( ! lo_BumpEditablePositionForward(context, state, &eptr, &position) )
  2094.         {
  2095.             return FALSE;
  2096.         }
  2097.     }
  2098.     else
  2099.     {
  2100.         position = lo_DecrementPosition(eptr, position + 1);
  2101.         /* This maps 0..length-1 into 1..length, which is OK. */
  2102.         position = lo_IncrementPosition(eptr, position);
  2103.     }
  2104.     *pElement = eptr;
  2105.     *pPosition = position;
  2106.  
  2107.     LO_ASSERT_POSITION2( context, eptr, position );
  2108.     return TRUE;
  2109. }
  2110.  
  2111. Bool
  2112. lo_NormalizeSelectionPoint(MWContext *context, lo_DocState * state, LO_Element** pEptr, int32* pPosition)
  2113. {
  2114.     /*
  2115.      * If the insert point is off the end of the element, make it the 0th selection of the next element.
  2116.      * If we can't normalize it, leave it alone.
  2117.      */
  2118.     Bool result = TRUE;
  2119.     if ( *pEptr != NULL )
  2120.     {
  2121.         int32 length = lo_GetElementLength(*pEptr);
  2122.         int32 newPosition = *pPosition;
  2123.         XP_ASSERT( 0 <= newPosition && newPosition <= length );
  2124.         if ( newPosition >= length )
  2125.         {
  2126.             if ( length <= 0 )
  2127.             {
  2128.                 /* There are some zero-length text elements in the browser, when
  2129.                  * tables are around. Also, the editor uses zero-length text
  2130.                  * elements for empty paragraphs and empty lines.
  2131.                  */
  2132.                 newPosition = 0;
  2133.             }
  2134.             else {
  2135.                 /* Needs to be internationalized */
  2136.                 newPosition = lo_GetLastCharBeginPosition(*pEptr);
  2137.                 result = lo_BumpNormalizedEditablePosition(context, state, pEptr, &newPosition, TRUE);
  2138.             }
  2139.             if ( result ) {
  2140.                 *pPosition = newPosition;
  2141.             }
  2142.         }
  2143.     }
  2144.     return result;
  2145. }
  2146.  
  2147. void
  2148. lo_NormalizeSelectionEnd(MWContext *context, lo_DocState * state, LO_Element** pEptr, int32* pPosition)
  2149. {
  2150.     /*
  2151.      * If the selection end point is off the end of the element, make it the last position of the element.
  2152.      */
  2153.     if ( *pEptr != NULL )
  2154.     {
  2155.         int32 length = lo_GetElementLength(*pEptr);
  2156.         XP_ASSERT( 0 <= *pPosition && *pPosition <= length );
  2157.         if ( *pPosition >= length )
  2158.         {
  2159.             *pPosition = lo_GetLastCharEndPosition(*pEptr);
  2160.         }
  2161.     }
  2162. }
  2163.  
  2164. PRIVATE
  2165. intn
  2166. lo_FancyHalfCompareSelections(LO_Element* start, int32 startPosition, LO_Element* end, int32 endPosition)
  2167. {
  2168.     int32 startID = start->lo_any.ele_id;
  2169.     int32 endID = end->lo_any.ele_id;
  2170.     if ( startID < endID )
  2171.     {
  2172.         if ( startID < (endID - 1) )
  2173.             return -1; /* Has to be less */
  2174.         if ( startPosition < lo_GetElementLength(start) )
  2175.             return -1;
  2176.         if ( endPosition > 0 )
  2177.             return -1;
  2178.         return 0;
  2179.     }
  2180.     XP_ASSERT(FALSE);
  2181.     return -1;
  2182. }
  2183.  
  2184. PRIVATE
  2185. intn
  2186. lo_FancyCompareSelections(MWContext *context, lo_DocState * state,
  2187.     LO_Element* start, int32 startPosition, LO_Element* end, int32 endPosition)
  2188. {
  2189. #if 0    /* We can't normalize past the end of the document. */
  2190.     Bool startIsEOD = ! lo_NormalizeSelectionPoint(context, state, &start, &startPosition);
  2191.     Bool endIsEOD = ! lo_NormalizeSelectionPoint(context, state, &end, &endPosition);
  2192.     if ( startIsEOD && endIsEOD ) return 0;
  2193.     else if ( startIsEOD ) return 1;
  2194.     else if ( endIsEOD ) return -1;
  2195.  
  2196.     return lo_compare_selections(start, startPosition, end, endPosition);
  2197. #endif
  2198.     int32 startID = 0;
  2199.     int32 endID = 0;
  2200.  
  2201.     /* Can not perform if no start or end element */
  2202.     if(!start || !end)
  2203.     {
  2204.         return 0;
  2205.     }
  2206.  
  2207.     /* Now safe to set IDs */
  2208.     startID = start->lo_any.ele_id;
  2209.     endID = end->lo_any.ele_id;
  2210.     
  2211.     if ( startID < endID )
  2212.     {
  2213.         return lo_FancyHalfCompareSelections(start, startPosition, end, endPosition);
  2214.     }
  2215.     else if ( startID == endID )
  2216.     {
  2217.         if ( startPosition < endPosition )
  2218.             return -1;
  2219.         else if ( startPosition == endPosition )
  2220.             return 0;
  2221.         return 1;
  2222.     }
  2223.     else /* startID > endID */
  2224.     {
  2225.         return - lo_FancyHalfCompareSelections(end, endPosition, start, startPosition);
  2226.     }
  2227. }
  2228.  
  2229.  
  2230. void
  2231. lo_StartNewSelection(MWContext *context, lo_DocState * state,
  2232.     LO_Element* eptr, int32 position)
  2233. {
  2234.     /*
  2235.      * If the user hasn't moved the insertion point yet, start a selection.
  2236.      */
  2237.     LO_Element* i_eptr = state->selection_new;
  2238.     int32 i_position = state->selection_new_pos;
  2239.     intn compare = lo_FancyCompareSelections(context, state, i_eptr, i_position, eptr, position);
  2240.     if ( compare < 0 )
  2241.     {
  2242.         /* old < now, so they're dragging towards the end of the document */
  2243.         lo_ConvertInsertPointToSelectionEnd(context, state, &eptr, &position);
  2244.         state->extending_start = FALSE;
  2245.         lo_FullSetSelection(context, state, i_eptr, i_position, eptr, position, FALSE);
  2246.     }
  2247.     else if ( compare == 0 )
  2248.     {
  2249.         /* The user hasn't moved the mouse far enough to select anything. */
  2250.         return;
  2251.     }
  2252.     else
  2253.     {
  2254.         /* Dragging towards start of document */
  2255.         lo_ConvertInsertPointToSelectionEnd(context, state, &i_eptr, &i_position);
  2256.         state->extending_start = TRUE;
  2257.         lo_FullSetSelection(context, state, eptr, position, i_eptr, i_position, TRUE);
  2258.     }
  2259. }
  2260.  
  2261. void
  2262. LO_ExtendSelection(MWContext *context, int32 x, int32 y)
  2263. {
  2264.     int32 doc_id;
  2265.     lo_TopState *top_state;
  2266.     lo_DocState *state;
  2267.     LO_Element *eptr;
  2268.     int32 position;
  2269.     LO_HitResult result;
  2270.  
  2271.     /*
  2272.      * Get the unique document ID, and retreive this
  2273.      * documents layout state.
  2274.      */
  2275.     doc_id = XP_DOCID(context);
  2276.     top_state = lo_FetchTopState(doc_id);
  2277.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  2278.     {
  2279.         return;
  2280.     }
  2281.     state = top_state->doc_state;
  2282.  
  2283.     LO_Hit(context, x, y, FALSE, &result, state->selection_layer);
  2284.  
  2285.     position = 0;
  2286.  
  2287.     eptr = NULL;
  2288.     switch ( result.type )
  2289.     {
  2290.     case LO_HIT_LINE:
  2291.         {
  2292.             switch ( result.lo_hitLine.region )
  2293.             {
  2294.             case LO_HIT_LINE_REGION_BEFORE:
  2295.                 {
  2296.                     /* Insertion point before first element of line */
  2297.                     eptr = result.lo_hitLine.selection.begin.element;
  2298.                     position = result.lo_hitLine.selection.begin.position;
  2299.                }
  2300.                 break;
  2301.             case LO_HIT_LINE_REGION_AFTER:
  2302.                 {
  2303.                     /* Insertion point after last element of line */
  2304.                      eptr = result.lo_hitLine.selection.end.element;
  2305.                     position = result.lo_hitLine.selection.end.position;
  2306.                     lo_ConvertSelectionEndToInsertPoint(context, state, &eptr, &position);
  2307.                     if ( eptr->type == LO_LINEFEED ) position = 0;  /* For editable line-feeds */
  2308.                }
  2309.                 break;
  2310.             default:
  2311.                 break;
  2312.             }
  2313.         }
  2314.         break;
  2315.     case LO_HIT_ELEMENT:
  2316.         {
  2317.             eptr = result.lo_hitElement.position.element;
  2318.             position  = result.lo_hitElement.position.position;
  2319.             switch ( result.lo_hitElement.region )
  2320.             {
  2321.             case LO_HIT_ELEMENT_REGION_BEFORE:
  2322.                 break;
  2323.             case LO_HIT_ELEMENT_REGION_MIDDLE:
  2324.                 {
  2325.                     /* The user wants this item selected.
  2326.                      *
  2327.                      * If the anchor point is before or at this item, the insert point should be
  2328.                      * after this item.
  2329.                      *
  2330.                      */
  2331.                     LO_Element* anchorElement;
  2332.                     int32 anchorPosition;
  2333.                     lo_GetAnchorPoint(context, state, &anchorElement, &anchorPosition);
  2334.                     if ( lo_FancyCompareSelections(context, state, anchorElement, anchorPosition, eptr, position) <= 0 )
  2335.                     {
  2336.                         lo_BumpEditablePositionForward(context, state, &eptr, &position);
  2337.                     }
  2338.                 }
  2339.                 break;
  2340.             case LO_HIT_ELEMENT_REGION_AFTER:
  2341.                 {
  2342.                     lo_BumpEditablePositionForward(context, state, &eptr, &position);
  2343.                 }
  2344.                 break;
  2345.             default:
  2346.                 break;
  2347.             }
  2348.         }
  2349.         break;
  2350.     default:
  2351.         break;
  2352.     }
  2353.     if (eptr == NULL)
  2354.     {
  2355.         return;
  2356.     }
  2357.  
  2358.     lo_ExtendSelectionToPosition2(context, top_state, state, eptr, position);
  2359. }
  2360.  
  2361. void
  2362. lo_ExtendSelectionToPosition2(MWContext *context, lo_TopState* top_state, lo_DocState *state, LO_Element* eptr, int32 position)
  2363. {
  2364.     /*
  2365.      * Are we starting a new selection?
  2366.      */
  2367.     if (state->selection_new != NULL)
  2368.     {
  2369.         lo_StartNewSelection(context, state, eptr, position);
  2370.         return;
  2371.     }
  2372.  
  2373.     /*
  2374.      * If we somehow got here without having any selection or started
  2375.      * selection, start it now where we are.
  2376.      */
  2377.     if (state->selection_start == NULL)
  2378.     {
  2379.         state->selection_start = eptr;
  2380.         state->selection_start_pos = position;
  2381.         state->selection_end = state->selection_start;
  2382.         state->selection_end_pos = state->selection_start_pos;
  2383.         state->extending_start = FALSE;
  2384.     }
  2385.  
  2386.     /*
  2387.      * By some horrendous bug the start was set and the end was not,
  2388.      * and there was no newly started selection.  Correct this now.
  2389.      */
  2390.     if (state->selection_end == NULL)
  2391.     {
  2392.         state->selection_end = state->selection_start;
  2393.         state->selection_end_pos = state->selection_start_pos;
  2394.         state->extending_start = FALSE;
  2395.     }
  2396.  
  2397.     {
  2398.         LO_Element* anchorElement;
  2399.         int32 anchorPosition;
  2400.         intn compare;
  2401.  
  2402.         lo_GetAnchorPoint(context, state, &anchorElement, &anchorPosition);
  2403.         compare = lo_FancyCompareSelections(context, state, eptr, position, anchorElement, anchorPosition );
  2404.  
  2405.         if ( compare == 0 )
  2406.         {
  2407.             /* The anchor point might not be editable -- it might be the start of a line feed.
  2408.              * So Jiggle it.
  2409.              */
  2410.             lo_JiggleToMakeEditable(context, state, &anchorElement, &anchorPosition, FALSE);
  2411.             lo_SetInsertPoint(context, top_state, anchorElement, anchorPosition, state->selection_layer);
  2412.         }
  2413.         else
  2414.         {
  2415.             LO_Element *changed_start, *changed_end;
  2416.             int32 changed_start_pos, changed_end_pos;
  2417.             Bool extendingStart;
  2418.             if ( compare < 0 )
  2419.             {
  2420.                 /* new position is less than anchor. So it's the start */
  2421.                 changed_start = eptr;
  2422.                 changed_start_pos = position;
  2423.                 changed_end = anchorElement;
  2424.                 changed_end_pos = anchorPosition;
  2425.                 extendingStart = TRUE;
  2426.             }
  2427.             else
  2428.             {
  2429.                 /* new position is greater than anchor. So it's the end */
  2430.                 changed_start = anchorElement;
  2431.                 changed_start_pos = anchorPosition;
  2432.                 changed_end = eptr;
  2433.                 changed_end_pos = position;
  2434.                 extendingStart = FALSE;
  2435.             }
  2436.             lo_ConvertInsertPointToSelectionEnd(context, state, &changed_end, &changed_end_pos);
  2437.  
  2438.             lo_FullSetSelection(context, state, changed_start, changed_start_pos,
  2439.                 changed_end, changed_end_pos, extendingStart);
  2440.         }
  2441.     }
  2442. }
  2443.  
  2444.  
  2445. void
  2446. LO_EndSelection(MWContext *context)
  2447. {
  2448.     int32 doc_id;
  2449.     lo_TopState *top_state;
  2450.     lo_DocState *state;
  2451.  
  2452.     /*
  2453.      * Get the unique document ID, and retreive this
  2454.      * documents layout state.
  2455.      */
  2456.     doc_id = XP_DOCID(context);
  2457.     top_state = lo_FetchTopState(doc_id);
  2458.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  2459.     {
  2460.         return;
  2461.     }
  2462.     state = top_state->doc_state;
  2463. }
  2464.  
  2465.  
  2466. #ifdef NOT_USED
  2467. static void
  2468. lo_copy_color(LO_Color *from_color, LO_Color *to_color)
  2469. {
  2470.     to_color->red = from_color->red;
  2471.     to_color->green = from_color->green;
  2472.     to_color->blue = from_color->blue;
  2473. }
  2474.  
  2475. #endif /* NOT_USED */
  2476.  
  2477.  
  2478. /*
  2479.  * If this element is part of a cell, return that cell.
  2480.  */
  2481. LO_Element *
  2482. lo_JumpCellWall(MWContext *context, lo_DocState *state, LO_Element *eptr)
  2483. {
  2484.     LO_Element *parent;
  2485.     LO_Element *cell = NULL;
  2486.     LO_Element *prev_eptr;
  2487.     LO_Element *stop_eptr;
  2488.     int32 x, y;
  2489.     int32 ret_x, ret_y;
  2490.     Bool guess_mode;
  2491.     CL_Layer *layer;
  2492.  
  2493.     /*
  2494.      * We only turn this on if we are desparate and think
  2495.      * we are in a cell with NO selectable elements!
  2496.      */
  2497.     guess_mode = FALSE;
  2498.  
  2499.     prev_eptr = NULL;
  2500.     x = eptr->lo_any.x + eptr->lo_any.x_offset;
  2501.     y = eptr->lo_any.y + eptr->lo_any.y_offset;
  2502.  
  2503.     /*
  2504.      * If this element is a zero width linefeed, it is unselectable, so
  2505.      * we need to special case this to work off the previous element
  2506.      * in the cell (if any).
  2507.      */
  2508.     if (eptr->lo_any.width <= 0)
  2509.     {
  2510.         prev_eptr = eptr->lo_any.prev;
  2511.         /*
  2512.          * Gads, you have have zero width text followed by a
  2513.          * zero width linefeed.  Move back until we get a real
  2514.          * width or can't go any furthur.
  2515.          */
  2516.         while ((prev_eptr != NULL)&&(prev_eptr->lo_any.width <= 0))
  2517.         {
  2518.             prev_eptr = prev_eptr->lo_any.prev;
  2519.         }
  2520.  
  2521.         if (prev_eptr != NULL)
  2522.         {
  2523.             x = prev_eptr->lo_any.x + prev_eptr->lo_any.x_offset;
  2524.             y = prev_eptr->lo_any.y + prev_eptr->lo_any.y_offset;
  2525.         }
  2526.         /*
  2527.          * If there is no previous element to the zero-width
  2528.          * one, guess in desperation by moving back one.
  2529.          * Probably there are no other elements in this
  2530.          * cell, go into guess_mode.
  2531.          */
  2532.         else
  2533.         {
  2534.             guess_mode = TRUE;
  2535.             /* 
  2536.              * We used to subtract 1 from the x pixel position, but
  2537.              * that didn't work in all cases and had the potential to
  2538.              * cause infinite loops. Now we don't and I feel a lot 
  2539.              * happier.
  2540.              */
  2541.         }
  2542.     }
  2543.  
  2544.     /*
  2545.      * figure out when to stop traversing inward.
  2546.      * If we have a prev_eptr then that is where we stop,
  2547.      * otherwise stop if we hit the starting element.
  2548.      */
  2549.     if (prev_eptr != NULL)
  2550.     {
  2551.         stop_eptr = prev_eptr;
  2552.     }
  2553.     else
  2554.     {
  2555.         stop_eptr = eptr;
  2556.     }
  2557.  
  2558.     parent = NULL;
  2559.     /* This deals with the case where the table is in a layer.
  2560.      * We want to search within the corresponding block and
  2561.      * not in the base document for the enclosing cell. 
  2562.      * BUGBUG This makes the assumption that if this table is
  2563.      * in a layer, that layer is part of the current selection
  2564.      * state. I don't know if this assumption is always true
  2565.      * for all invocations of this function.
  2566.      * joki: changing function to work based on layer key focus
  2567.      * instead of selected layer so that form elements will be
  2568.      * tabable in tables. 5/25/97.
  2569.      */
  2570.      if (context->compositor && !(CL_IsKeyEventGrabber(context->compositor, NULL))) {
  2571.     layer = CL_GetKeyEventGrabber(context->compositor); 
  2572.         
  2573.     if (layer && LO_GetIdFromLayer(context, layer) != LO_DOCUMENT_LAYER_ID) {
  2574.         LO_CellStruct *layer_cell = lo_GetCellFromLayer(context, layer);
  2575.         if (layer_cell)
  2576.         cell = lo_XYToCellElement(context,
  2577.                       state,
  2578.                       layer_cell,
  2579.                       x, y, TRUE, FALSE, FALSE);
  2580.     }
  2581.     }
  2582.  
  2583.     if (cell == NULL)
  2584.         cell = lo_XYToDocumentElement(context, state,
  2585.                                       x, y, TRUE, FALSE, FALSE, 
  2586.                                       &ret_x, &ret_y);
  2587.     while ((cell != NULL)&&(cell != stop_eptr)&&(cell->type == LO_CELL))
  2588.     {
  2589.         parent = cell;
  2590.         cell = lo_XYToCellElement(context, state, (LO_CellStruct *)cell,
  2591.             x, y, TRUE, FALSE, FALSE);
  2592.     }
  2593.  
  2594.     /*
  2595.      * If we've traversed to our stopping point.
  2596.      */
  2597.     if (cell == stop_eptr)
  2598.     {
  2599.         /*
  2600.          * If the stopping point has a cell parent, return it.
  2601.          */
  2602.         if ((parent != NULL)&&(parent->type == LO_CELL))
  2603.         {
  2604.             return(parent);
  2605.         }
  2606.         /*
  2607.          * Else we aren't in a cell, return the original element.
  2608.          */
  2609.         else
  2610.         {
  2611.             return(eptr);
  2612.         }
  2613.     }
  2614.     /*
  2615.      * Else if we are in guess mode, we found a valid parent
  2616.      * cell that appears to have no children, then we need to check
  2617.      * if the first child of the parent is our stop_eptr, and if
  2618.      * so, return the cell parent.
  2619.      */
  2620.     else if ((guess_mode != FALSE)&&(cell == NULL)&&(parent != NULL)&&
  2621.         (parent->type == LO_CELL))
  2622.     {
  2623.         if (parent->lo_cell.cell_list == stop_eptr)
  2624.         {
  2625.             return(parent);
  2626.         }
  2627.  
  2628.         /*
  2629.          * If we get here we don't know what we found, so just
  2630.          * return.
  2631.          */
  2632.         return(eptr);
  2633.     }
  2634.     /* If we found a parent that's a cell, then return it.
  2635.      * I think this case happens when we hit an element
  2636.      * that has the same coordinates are the element we
  2637.      * were given. An example would be a zero-length
  2638.      * text element before a linefeed. Maybe we should
  2639.      * change the search algorithm to skip zero-lenght elements?
  2640.      *
  2641.      */
  2642.     else if ( (parent != NULL) && (parent->type == LO_CELL))
  2643.     {
  2644.         return parent;
  2645.     }
  2646.     /*
  2647.      * Else, we've missed the stopping point for some reason.
  2648.      * Assume we aren't in a cell.
  2649.      */
  2650.     else
  2651.     {
  2652.         return(eptr);
  2653.     }
  2654. }
  2655.  
  2656.  
  2657. void
  2658. lo_SetSelect(MWContext *context, lo_DocState *state,
  2659.     LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos,
  2660.     Bool on)
  2661. {
  2662.         LO_Element *eptr;
  2663.  
  2664.     if ((start == NULL)||(end == NULL))
  2665.     {
  2666.         return;
  2667.     }
  2668.  
  2669.     eptr = start;
  2670.         while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
  2671.         {
  2672.         int32 last_id;
  2673.  
  2674.                 switch (eptr->type)
  2675.                 {
  2676.             case LO_TEXT:
  2677.             if (eptr->lo_text.text != NULL)
  2678.             {
  2679.                 int32 p1, p2;
  2680.  
  2681.                 if (eptr == start)
  2682.                 {
  2683.                     p1 = start_pos;
  2684.                 }
  2685.                 else
  2686.                 {
  2687.                     p1 = 0;
  2688.                 }
  2689.  
  2690.                 if (eptr == end)
  2691.                 {
  2692.                     p2 = end_pos;
  2693.                 }
  2694.                 else
  2695.                 {
  2696.                     p2 = lo_GetLastCharEndPosition(eptr);
  2697.                 }
  2698.                 if (p2 < p1)
  2699.                 {
  2700.                     p2 = p1;
  2701.                 }
  2702.  
  2703.                 if (on != FALSE)
  2704.                 {
  2705.                 eptr->lo_text.ele_attrmask |= LO_ELE_SELECTED;
  2706.                 eptr->lo_text.sel_start = (intn) p1;
  2707.                 eptr->lo_text.sel_end = (intn) p2;
  2708.                 }
  2709.                 else
  2710.                 {
  2711.                 eptr->lo_text.ele_attrmask &= (~(LO_ELE_SELECTED));
  2712.                 eptr->lo_text.sel_start = -1;
  2713.                 eptr->lo_text.sel_end = -1;
  2714.                 }
  2715.             }
  2716.             break;
  2717.             case LO_LINEFEED:
  2718.             if (on != FALSE)
  2719.             {
  2720.                 eptr->lo_linefeed.ele_attrmask |= LO_ELE_SELECTED;
  2721.                 eptr->lo_linefeed.sel_start = 0;
  2722.                 eptr->lo_linefeed.sel_end = 0;
  2723.             }
  2724.             else
  2725.             {
  2726.                 eptr->lo_linefeed.ele_attrmask &= (~(LO_ELE_SELECTED));
  2727.                 eptr->lo_linefeed.sel_start = -1;
  2728.                 eptr->lo_linefeed.sel_end = -1;
  2729.             }
  2730.             break;
  2731.             case LO_IMAGE:
  2732. #ifdef EDITOR
  2733.             if ( EDT_IS_EDITOR(context) )
  2734.             {
  2735.                 if (on != FALSE)
  2736.                 {
  2737.                     eptr->lo_image.ele_attrmask |= LO_ELE_SELECTED;
  2738.                     eptr->lo_image.sel_start = 0;
  2739.                     eptr->lo_image.sel_end = 0;
  2740.                 }
  2741.                 else
  2742.                 {
  2743.                     eptr->lo_image.ele_attrmask &= (~(LO_ELE_SELECTED));
  2744.                     eptr->lo_image.sel_start = -1;
  2745.                     eptr->lo_image.sel_end = -1;
  2746.                   }
  2747.            }
  2748.             break;
  2749. #endif /* EDITOR */
  2750.             case LO_HRULE:
  2751. #ifdef EDITOR
  2752.             if ( EDT_IS_EDITOR(context) )
  2753.             {
  2754.                 if (on != FALSE)
  2755.                 {
  2756.                     eptr->lo_hrule.ele_attrmask |= LO_ELE_SELECTED;
  2757.                      eptr->lo_hrule.sel_start = 0;
  2758.                     eptr->lo_hrule.sel_end = 0;
  2759.                    }
  2760.                 else
  2761.                 {
  2762.                     eptr->lo_hrule.ele_attrmask &= (~(LO_ELE_SELECTED));
  2763.                      eptr->lo_hrule.sel_start = -1;
  2764.                     eptr->lo_hrule.sel_end = -1;
  2765.                  }
  2766.            }
  2767.             break;
  2768. #endif /* EDITOR */
  2769.             case LO_FORM_ELE:
  2770. #ifdef EDITOR
  2771.             if ( EDT_IS_EDITOR(context) )
  2772.             {
  2773.                 if (on != FALSE)
  2774.                 {
  2775.                     eptr->lo_form.ele_attrmask |= LO_ELE_SELECTED;
  2776.                     eptr->lo_form.sel_start = 0;
  2777.                     eptr->lo_form.sel_end = 0;
  2778.                 }
  2779.                 else
  2780.                 {
  2781.                     eptr->lo_form.ele_attrmask &= (~(LO_ELE_SELECTED));
  2782.                      eptr->lo_form.sel_start = -1;
  2783.                     eptr->lo_form.sel_end = -1;
  2784.                  }
  2785.            }
  2786.             break;
  2787. #endif /* EDITOR */
  2788.             case LO_BULLET:
  2789.             case LO_SUBDOC:
  2790.             case LO_TABLE:
  2791.             default:
  2792.              break;
  2793.                 }
  2794.  
  2795.         last_id = eptr->lo_any.ele_id;
  2796.  
  2797.         /*
  2798.          * Jump cell boundries if there is one between start
  2799.          * and end.
  2800.          */
  2801.         if ((eptr->lo_any.next == NULL)&&(eptr != end))
  2802.         {
  2803.             eptr = lo_JumpCellWall(context, state, eptr);
  2804.         }
  2805.  
  2806.                 eptr = eptr->lo_any.next;
  2807.  
  2808.         /*
  2809.          * When we walk onto a cell, we need to walk into
  2810.          * it if it isn't empty.
  2811.          */
  2812.         if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
  2813.             (eptr->lo_cell.cell_list != NULL))
  2814.         {
  2815.             eptr = eptr->lo_cell.cell_list;
  2816.         }
  2817.  
  2818.         /*
  2819.          * We don't want infinite loops.
  2820.          * If the element ID hasn't progressed, something
  2821.          * serious is wrong, and we should punt.
  2822.          */
  2823.         if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
  2824.         {
  2825. #ifdef DEBUG
  2826. XP_TRACE(("Selection loop avoidance 1\n"));
  2827. #endif /* DEBUG */
  2828.             break;
  2829.         }
  2830.         }
  2831. }
  2832.  
  2833. typedef enum {
  2834.     HL_STARTED,
  2835.     HL_NOT_STARTED,
  2836.     HL_COMPLETED
  2837. } LO_HighlightState;
  2838.  
  2839. /* Highlight a range of elements from within a list of layout
  2840.    elements.  Return values:
  2841.  
  2842.    HL_STARTED      At least one element highlighted from list, but
  2843.                    last element in range was not encountered on list.
  2844.  
  2845.    HL_NOT_STARTED  Specified range did not contain any of the range
  2846.                    of elements specified in the arguments.
  2847.  
  2848.    HL_COMPLETED    Specified range of elements has been highlighted. */
  2849. static LO_HighlightState
  2850. lo_HighlightElementList(MWContext *context,
  2851.                         int32 base_x, int32 base_y,
  2852.                         LO_Element *list,
  2853.                         LO_Element *start, int32 start_pos,
  2854.                         LO_Element *end, int32 end_pos,
  2855.                         CL_Layer *layer,
  2856.                         Bool on)
  2857. {
  2858.     LO_Element *eptr;
  2859.     int16 charset;
  2860.     int32 p1, p2;
  2861.     LO_HighlightState highlight_state;
  2862.  
  2863.     if (list == NULL)
  2864.         return HL_COMPLETED;
  2865.  
  2866.     /* Locate first element to be highlighted in list */
  2867.     eptr = list;
  2868.     while (eptr && eptr != start) {
  2869.         if (eptr->type == LO_CELL) {
  2870.             int32 x_offset = 0;
  2871.             int32 y_offset = 0;
  2872.             CL_Layer *cell_layer = layer;
  2873.  
  2874.             /* LO_CELLs can be either ordinary table cells or
  2875.                containers for in-flow layers */
  2876.             if (eptr->lo_cell.cell_inflow_layer) {
  2877.                 cell_layer = eptr->lo_cell.cell_inflow_layer;
  2878.                 lo_GetLayerXYShift(cell_layer, &x_offset, &y_offset);
  2879.             }
  2880.  
  2881.             if (eptr->lo_cell.cell_list) {
  2882.                 highlight_state=lo_HighlightElementList(context, 
  2883.                                                         base_x - x_offset,
  2884.                                                         base_y - y_offset,
  2885.                                                         eptr->lo_cell.cell_list,
  2886.                                                         start, start_pos,
  2887.                                                         end, end_pos, 
  2888.                                                         cell_layer, on);
  2889.                 if (highlight_state == HL_COMPLETED)
  2890.                     return HL_COMPLETED;
  2891.                 if (highlight_state == HL_STARTED) {
  2892.                     eptr = eptr->lo_any.next;
  2893.                     break;
  2894.                 }
  2895.             }
  2896.         }
  2897.         eptr = eptr->lo_any.next;
  2898.     }
  2899.     if (!eptr)
  2900.         return HL_NOT_STARTED;
  2901.  
  2902.     /* Paint highlighted elements */
  2903.     while ((eptr != NULL) && (eptr->lo_any.ele_id <= end->lo_any.ele_id))
  2904.     {
  2905.         int32 last_id;
  2906.  
  2907.         switch (eptr->type)
  2908.         {
  2909.         case LO_TEXT:
  2910.             if (eptr->lo_text.text == NULL)
  2911.                 break;
  2912.             
  2913.             if (eptr == start)
  2914.                 p1 = start_pos;
  2915.             else
  2916.                 p1 = 0;
  2917.  
  2918.             if (eptr == end)
  2919.                 p2 = end_pos;
  2920.             else
  2921.                 p2 = lo_GetLastCharEndPosition(eptr);
  2922.  
  2923.             if (p2 < p1)
  2924.                 p2 = p1;
  2925.  
  2926.             if ( p2 >= eptr->lo_text.text_len )
  2927.                 p2 = lo_GetLastCharEndPosition(eptr);
  2928.  
  2929.             if (on != FALSE)
  2930.             {
  2931.                 eptr->lo_text.ele_attrmask |= LO_ELE_SELECTED;
  2932.                 eptr->lo_text.sel_start = (intn) p1;
  2933.                 eptr->lo_text.sel_end = (intn) p2;
  2934.             }
  2935.             else
  2936.             {
  2937.                 eptr->lo_text.ele_attrmask &= (~(LO_ELE_SELECTED));
  2938.                 eptr->lo_text.sel_start = -1;
  2939.                 eptr->lo_text.sel_end = -1;
  2940.             }
  2941.  
  2942.             charset = ((LO_TextStruct *) eptr)->text_attr->charset;
  2943.             if ((eptr == start) || ((eptr == end) &&
  2944.                                     INTL_CharSetType(charset) != SINGLEBYTE))
  2945.             {
  2946.                 /* ugly processing for multibyte here    */
  2947.                 char *string;
  2948.                 int n;
  2949.  
  2950.                 PA_LOCK(string, char *, eptr->lo_text.text);
  2951.  
  2952.                 /*
  2953.                  * find beginning of first character
  2954.                  */
  2955.                 switch (n = INTL_NthByteOfChar(charset, string, (int)(p1+1)))
  2956.                 {
  2957.                 case 0:
  2958.                 case 1:
  2959.                     break;
  2960.                 default:
  2961.                     p1 -= (n - 1);
  2962.                     if (p1 < 0)
  2963.                         p1 = 0;
  2964.  
  2965.                     break;
  2966.                 }
  2967.  
  2968.                 /*
  2969.                  * find end of last character
  2970.                  */
  2971.                 switch (n = INTL_NthByteOfChar(charset, string, (int)(p2+1)))
  2972.                 {
  2973.                 case 0:
  2974.                     break;
  2975.                 default:
  2976.                     p2 -= (n - 1);
  2977.                     if (p2 < 0)
  2978.                         p2 = 0;
  2979.  
  2980.                     /* FALL THROUGH */
  2981.                 case 1:
  2982.                     p2 += INTL_IsLeadByte(charset, string[p2]);
  2983.                     if (p2 > lo_GetLastCharEndPosition(eptr))
  2984.                     {
  2985.                         p2 = lo_GetLastCharEndPosition(eptr);
  2986.                     }
  2987.                     break;
  2988.                 }
  2989.  
  2990.                 PA_UNLOCK(eptr->lo_text.text);
  2991.             }
  2992.             
  2993.             if (p1 <= p2)
  2994.             {
  2995.                 lo_DisplaySubtext(context,
  2996.                                   (LO_TextStruct *)eptr, p1, p2, 
  2997.                                   !on,
  2998.                                   layer);
  2999.             }
  3000.             break;
  3001.  
  3002.         case LO_LINEFEED:
  3003.             if (on != FALSE)
  3004.             {
  3005.                 eptr->lo_linefeed.ele_attrmask |= LO_ELE_SELECTED;
  3006.                 eptr->lo_linefeed.sel_start = 0;
  3007.                 eptr->lo_linefeed.sel_end = 0;
  3008.             }
  3009.             else
  3010.             {
  3011.                 eptr->lo_linefeed.ele_attrmask &= (~(LO_ELE_SELECTED));
  3012.                 eptr->lo_linefeed.sel_start = -1;
  3013.                 eptr->lo_linefeed.sel_end = -1;
  3014.             }
  3015.  
  3016.             lo_RefreshElement(eptr, layer, FALSE);
  3017.             break;
  3018.  
  3019.         case LO_HRULE:
  3020. #ifdef EDITOR
  3021.             if ( EDT_IS_EDITOR(context) )
  3022.             {
  3023.                 if (on != FALSE)
  3024.                 {
  3025.                     eptr->lo_hrule.ele_attrmask |= LO_ELE_SELECTED;
  3026.                     eptr->lo_hrule.sel_start = 0;
  3027.                     eptr->lo_hrule.sel_end = 0;
  3028.                 }
  3029.                 else
  3030.                 {
  3031.                     eptr->lo_hrule.ele_attrmask &= (~(LO_ELE_SELECTED));
  3032.                     eptr->lo_hrule.sel_start = -1;
  3033.                     eptr->lo_hrule.sel_end = -1;
  3034.                 }
  3035.  
  3036.                 lo_RefreshElement(eptr, layer, FALSE);
  3037.             }
  3038.             break;
  3039. #endif
  3040.         case LO_FORM_ELE:
  3041. #ifdef EDITOR
  3042.             if ( EDT_IS_EDITOR(context) )
  3043.             {
  3044.                 if (on != FALSE)
  3045.                 {
  3046.                     eptr->lo_form.ele_attrmask |= LO_ELE_SELECTED;
  3047.                     eptr->lo_form.sel_start = 0;
  3048.                     eptr->lo_form.sel_end = 0;
  3049.                 }
  3050.                 else
  3051.                 {
  3052.                     eptr->lo_form.ele_attrmask &= (~(LO_ELE_SELECTED));
  3053.                     eptr->lo_form.sel_start = -1;
  3054.                     eptr->lo_form.sel_end = -1;
  3055.                 }
  3056.  
  3057.                 lo_DisplayFormElement(context, (LO_FormElementStruct *)eptr);
  3058.             }
  3059.             break;
  3060. #endif
  3061.         case LO_IMAGE:
  3062. #ifdef EDITOR
  3063.             if ( EDT_IS_EDITOR(context) )
  3064.             {
  3065.                 if (on != FALSE)
  3066.                 {
  3067.                     eptr->lo_image.ele_attrmask |= LO_ELE_SELECTED;
  3068.                     eptr->lo_image.sel_start = 0;
  3069.                     eptr->lo_image.sel_end = 0;
  3070.                 }
  3071.                 else
  3072.                 {
  3073.                     eptr->lo_image.ele_attrmask &= (~(LO_ELE_SELECTED));
  3074.                     eptr->lo_image.sel_start = -1;
  3075.                     eptr->lo_image.sel_end = -1;
  3076.                 }
  3077.  
  3078.                 {
  3079.                     LO_ImageStruct *lo_image = (LO_ImageStruct*)eptr;
  3080.                     XP_Rect rect;
  3081.                     
  3082.                     rect.left = 0;
  3083.                     rect.top = 0;
  3084.                     rect.right = lo_image->width;
  3085.                     rect.bottom = lo_image->height;
  3086.  
  3087.                     CL_UpdateLayerRect(context->compositor, lo_image->layer,
  3088.                                        &rect, (PRBool)FALSE);
  3089.                 }
  3090.             }
  3091. #endif
  3092.             break;
  3093.  
  3094.         case LO_CELL:
  3095.         {
  3096.             LO_CellStruct *cell = &eptr->lo_cell;
  3097.             int32 x_offset = 0;
  3098.             int32 y_offset = 0;
  3099.             CL_Layer *cell_layer = layer;
  3100.  
  3101.             /* LO_CELLs can be either ordinary table cells or
  3102.                containers for in-flow layers */
  3103.             if (cell->cell_inflow_layer) {
  3104.                 cell_layer = cell->cell_inflow_layer;
  3105.                 lo_GetLayerXYShift(cell_layer, &x_offset, &y_offset);
  3106.             }
  3107.             
  3108.             if (!eptr->lo_cell.cell_list)
  3109.                 break;
  3110.             
  3111.             highlight_state = lo_HighlightElementList(context,
  3112.                                                       base_x - x_offset,
  3113.                                                       base_y - y_offset,
  3114.                                                       eptr->lo_cell.cell_list,
  3115.                                                       eptr->lo_cell.cell_list, 0,
  3116.                                                       end, end_pos, 
  3117.                                                       cell_layer, on);
  3118.             if (highlight_state == HL_COMPLETED) {
  3119.                 return HL_COMPLETED;
  3120.             }
  3121.             break;
  3122.         }
  3123.  
  3124.         case LO_SUBDOC:
  3125.         case LO_TABLE:
  3126.         case LO_BULLET:
  3127.         default:
  3128.             break;
  3129.         }
  3130.  
  3131.         last_id = eptr->lo_any.ele_id;
  3132.  
  3133.         eptr = eptr->lo_any.next;
  3134.  
  3135.         /*
  3136.          * We don't want infinite loops.
  3137.          * If the element ID hasn't progressed, something
  3138.          * serious is wrong, and we should punt.
  3139.          */
  3140.         if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
  3141.         {
  3142. #ifdef DEBUG
  3143.             XP_TRACE(("Selection loop avoidance 2\n"));
  3144. #endif /* DEBUG */
  3145.             break;
  3146.         }
  3147.     }
  3148.  
  3149.     /* We ran off the list without reaching the last element in the
  3150.        selection range, so tell our caller that there's more work to do. */
  3151.     if (!eptr)
  3152.         return HL_STARTED;
  3153.  
  3154.     /* We reached the last element in the selection range.  We're done. */
  3155.     return HL_COMPLETED;
  3156. }
  3157.  
  3158.  
  3159. void
  3160. lo_HighlightSelect(MWContext *context, lo_DocState *state,
  3161.                    LO_Element *start, int32 start_pos,
  3162.                    LO_Element *end, int32 end_pos,
  3163.                    Bool on)
  3164. {
  3165.     LO_Element **line_array, *list;
  3166.     LO_CellStruct *layer_cell;
  3167.  
  3168.     if ((start == NULL) || (end == NULL))
  3169.         return;
  3170.  
  3171.     /* 
  3172.      * Do a synchronous composite to flush any other drawing 
  3173.      * request.  Since we're going to replace the painter_func
  3174.      * of state->selection_layer, we don't want to ignore other
  3175.      * requests that might require this layer to draw.  This call
  3176.      * must happen before the text element is modified in any way.
  3177.      */
  3178.     if (context->compositor)
  3179.         CL_CompositeNow(context->compositor);
  3180.  
  3181.     /* The stinkin' editor doesn't set the selection layer yet. */
  3182.     if (!state->selection_layer) 
  3183.         state->selection_layer = CL_FindLayer(context->compositor, LO_BODY_LAYER_NAME);
  3184.  
  3185.     layer_cell = lo_GetCellFromLayer(context, state->selection_layer);
  3186.     if (layer_cell == NULL) {
  3187.         XP_LOCK_BLOCK(line_array, LO_Element**, state->line_array );
  3188.         if (line_array)
  3189.             list = line_array[0];
  3190.         else
  3191.             list = NULL;
  3192.         XP_UNLOCK_BLOCK( state->line_array );
  3193.     } else 
  3194.         list = layer_cell->cell_list;
  3195.  
  3196.     lo_HighlightElementList(context, state->base_x, state->base_y, list,
  3197.                             start, start_pos, end, end_pos, 
  3198.                             state->selection_layer, on);
  3199.  
  3200.     /* On the Mac, we don't run timeouts during a selection, so the
  3201.        compositor won't run.  Therefore we need to force any new
  3202.        selection to paint now. */
  3203.     if (context->compositor)
  3204.         CL_CompositeNow(context->compositor);
  3205. }
  3206.  
  3207. void
  3208. LO_HighlightSelection(MWContext *context, Bool on)
  3209. {
  3210.         int32 doc_id;
  3211.     lo_TopState *top_state;
  3212.         lo_DocState *state;
  3213.         LO_Element *start, *end;
  3214.     int32 start_pos, end_pos;
  3215.  
  3216.         /*
  3217.          * Get the unique document ID, and retreive this
  3218.          * documents layout state.
  3219.          */
  3220.         doc_id = XP_DOCID(context);
  3221.     top_state = lo_FetchTopState(doc_id);
  3222.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3223.         {
  3224.                 return;
  3225.         }
  3226.     state = top_state->doc_state;
  3227.  
  3228.         if ((state->selection_start == NULL)||(state->selection_end == NULL))
  3229.         {
  3230.                 return;
  3231.         }
  3232.  
  3233.         start = state->selection_start;
  3234.     start_pos = state->selection_start_pos;
  3235.         end = state->selection_end;
  3236.     end_pos = state->selection_end_pos;
  3237.  
  3238.     lo_HighlightSelect(context, state, start, start_pos, end, end_pos,
  3239.                     on);
  3240. }
  3241.  
  3242.  
  3243. void
  3244. LO_ClearSelection(MWContext *context)
  3245. {
  3246.     int32 doc_id;
  3247.     lo_TopState *top_state;
  3248.     lo_DocState *state;
  3249.  
  3250.     /*
  3251.      * Get the unique document ID, and retreive this
  3252.      * documents layout state.
  3253.      */
  3254.     doc_id = XP_DOCID(context);
  3255.     top_state = lo_FetchTopState(doc_id);
  3256.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3257.     {
  3258.         return;
  3259.     }
  3260.     state = top_state->doc_state;
  3261.  
  3262.     LO_HighlightSelection(context, FALSE);
  3263.     state->selection_start = NULL;
  3264.     state->selection_start_pos = 0;
  3265.     state->selection_end = NULL;
  3266.     state->selection_end_pos = 0;
  3267.     state->selection_layer = NULL;
  3268. }
  3269.  
  3270.  
  3271. PRIVATE
  3272. XP_Block
  3273. lo_SelectionToText(MWContext *context, lo_DocState *state,
  3274.     LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos)
  3275. {
  3276.         LO_Element *eptr;
  3277.     LO_TextStruct tmp_text;
  3278.     LO_TextInfo text_info;
  3279.     LO_TextAttr tmp_attr;
  3280.     int32 space_width;
  3281.     int32 length;
  3282.     Bool indent_counts;
  3283.     PA_Block sbuff;
  3284.     XP_Block buff;
  3285.     char *str;
  3286.     char *tptr;
  3287. /*    int16 charset;*/
  3288.  
  3289.         if ((start == NULL)||(end == NULL))
  3290.         {
  3291.                 return(NULL);
  3292.         }
  3293.  
  3294.     /*
  3295.      * All this work is to get the width of a " " in the default
  3296.      * fixed font.
  3297.      */
  3298.     memset (&tmp_text, 0, sizeof (tmp_text));
  3299.     sbuff = PA_ALLOC(1);
  3300.     if (sbuff == NULL)
  3301.     {
  3302.         return(NULL);
  3303.     }
  3304.     PA_LOCK(str, char *, sbuff);
  3305.     str[0] = ' ';
  3306.     PA_UNLOCK(sbuff);
  3307.     tmp_text.text = sbuff;
  3308.     tmp_text.text_len = 1;
  3309.     /*
  3310.      * Fill in default font information.
  3311.      */
  3312.     lo_SetDefaultFontAttr(state, &tmp_attr, context);
  3313.     tmp_attr.fontmask |= LO_FONT_FIXED;
  3314.     tmp_text.text_attr = lo_FetchTextAttr(state, &tmp_attr);
  3315.     FE_GetTextInfo(context, &tmp_text, &text_info);
  3316.     PA_FREE(sbuff);
  3317.     space_width = text_info.max_width;
  3318.     if (space_width <= 0)
  3319.     {
  3320.         space_width = 2;
  3321.     }
  3322.  
  3323.     indent_counts = FALSE;
  3324.     length = 0;
  3325.     eptr = start;
  3326.         while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
  3327.         {
  3328.         int32 last_id;
  3329.  
  3330.                 switch (eptr->type)
  3331.                 {
  3332.             case LO_TEXT:
  3333.             if (eptr->lo_text.text != NULL)
  3334.             {
  3335.                 if (indent_counts != FALSE)
  3336.                 {
  3337.                 int32 width;
  3338.  
  3339.                 width = eptr->lo_text.x +
  3340.                     eptr->lo_text.x_offset -
  3341.                     state->win_left;
  3342.                 width = (width + space_width - 1) / space_width;
  3343.                 if (width < 0)
  3344.                 {
  3345.                     width = 0;
  3346.                 }
  3347.                 length += width;
  3348.                 indent_counts = FALSE;
  3349.                 }
  3350.  
  3351.                 if ((eptr == start)||(eptr == end))
  3352.                 {
  3353.                     int32 p1, p2;
  3354.  
  3355.                     if (eptr == start)
  3356.                     {
  3357.                     p1 = start_pos;
  3358.                     }
  3359.                     else
  3360.                     {
  3361.                     p1 = 0;
  3362.                     }
  3363.  
  3364.                     if (eptr == end)
  3365.                     {
  3366.                     char *string;
  3367.  
  3368.                     PA_LOCK(string, char *, eptr->lo_text.text);
  3369.                     /*  charset = eptr->lo_text.text_attr->charset;
  3370.                      *    p2 = end_pos + INTL_IsLeadByte(charset, string[end_pos]);
  3371.                      *    
  3372.                      *    p2 already point to the end of the selection
  3373.                      *    calling INTL_IsLeadByte(charset, string[end_pos]) is wrong here.
  3374.                      */
  3375.                     p2 = end_pos;
  3376.                     PA_UNLOCK(eptr->lo_text.text);
  3377.                     if (p2 > lo_GetLastCharEndPosition(eptr))
  3378.                     {
  3379.                     p2 = lo_GetLastCharEndPosition(eptr);
  3380.                     }
  3381.                     }
  3382.                     else
  3383.                     {
  3384.                     p2 = lo_GetLastCharEndPosition(eptr);
  3385.                     }
  3386.                     if (p2 < p1)
  3387.                     {
  3388.                     p2 = p1;
  3389.                     }
  3390.  
  3391.                 length += (p2 - p1 + 1);
  3392.                 }
  3393.                 else
  3394.                 {
  3395.                 length += eptr->lo_text.text_len;
  3396.                 }
  3397.             }
  3398.             break;
  3399.             case LO_LINEFEED:
  3400.             length += LINEBREAK_LEN;
  3401.             indent_counts = TRUE;
  3402.             break;
  3403.             case LO_HRULE:
  3404.             case LO_FORM_ELE:
  3405.             case LO_BULLET:
  3406.             case LO_IMAGE:
  3407.             case LO_SUBDOC:
  3408.             case LO_TABLE:
  3409.             default:
  3410.             break;
  3411.                 }
  3412.  
  3413.         last_id = eptr->lo_any.ele_id;
  3414.  
  3415.         /*
  3416.          * Jump cell boundries if there is one between start
  3417.          * and end.
  3418.          */
  3419.         if ((eptr->lo_any.next == NULL)&&(eptr != end))
  3420.         {
  3421.             eptr = lo_JumpCellWall(context, state, eptr);
  3422.         }
  3423.  
  3424.                 eptr = eptr->lo_any.next;
  3425.  
  3426.         /*
  3427.          * When we walk onto a cell, we need to walk into
  3428.          * it if it isn't empty.
  3429.          */
  3430.         if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
  3431.             (eptr->lo_cell.cell_list != NULL))
  3432.         {
  3433.             eptr = eptr->lo_cell.cell_list;
  3434.         }
  3435.  
  3436.         /*
  3437.          * We don't want infinite loops.
  3438.          * If the element ID hasn't progressed, something
  3439.          * serious is wrong, and we should punt.
  3440.          */
  3441.         if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
  3442.         {
  3443. #ifdef DEBUG
  3444. XP_TRACE(("Selection loop avoidance 3\n"));
  3445. #endif /* DEBUG */
  3446.             break;
  3447.         }
  3448.         }
  3449.     length++;
  3450.  
  3451. #ifdef XP_WIN16
  3452.     if (length > SIZE_LIMIT)
  3453.     {
  3454.         length = SIZE_LIMIT;
  3455.     }
  3456. #endif /* XP_WIN16 */
  3457.  
  3458.     buff = XP_ALLOC_BLOCK(length * sizeof(char));
  3459.     if (buff == NULL)
  3460.     {
  3461.         return(NULL);
  3462.     }
  3463.     XP_LOCK_BLOCK(str, char *, buff);
  3464.  
  3465.     tptr = str;
  3466.     indent_counts = FALSE;
  3467.     length = 0;
  3468.     eptr = start;
  3469.         while ((eptr != NULL)&&(eptr->lo_any.ele_id <= end->lo_any.ele_id))
  3470.         {
  3471.         int32 last_id;
  3472.  
  3473.                 switch (eptr->type)
  3474.                 {
  3475.             case LO_TEXT:
  3476.             if (eptr->lo_text.text != NULL)
  3477.             {
  3478.                 char *text;
  3479.  
  3480.                 if (indent_counts != FALSE)
  3481.                 {
  3482.                 int32 width;
  3483.                 int32 i;
  3484.  
  3485.                 width = eptr->lo_text.x +
  3486.                     eptr->lo_text.x_offset -
  3487.                     state->win_left;
  3488.                 width = (width + space_width - 1) / space_width;
  3489.                 if (width < 0)
  3490.                 {
  3491.                     width = 0;
  3492.                 }
  3493.                 length += width;
  3494. #ifdef XP_WIN16
  3495.                 if (length > SIZE_LIMIT)
  3496.                 {
  3497.                     length -= width;
  3498.                     break;
  3499.                 }
  3500. #endif /* XP_WIN16 */
  3501.                 for (i=0; i<width; i++)
  3502.                 {
  3503.                     XP_BCOPY(" ", tptr, 1);
  3504.                     tptr++;
  3505.                 }
  3506.                 indent_counts = FALSE;
  3507.                 }
  3508.  
  3509.                 if ((eptr == start)||(eptr == end))
  3510.                 {
  3511.                     int32 p1, p2, len;
  3512.                     int32 maxPos = lo_GetLastCharEndPosition(eptr);
  3513.  
  3514.                     if (eptr == start)
  3515.                     {
  3516.                         p1 = start_pos;
  3517.                     }
  3518.                     else
  3519.                     {
  3520.                         p1 = 0;
  3521.                     }
  3522.  
  3523.                     if (eptr == end)
  3524.                     {
  3525.                         char *string;
  3526.  
  3527.                         PA_LOCK(string, char *, eptr->lo_text.text);
  3528.                     /*  charset = eptr->lo_text.text_attr->charset;
  3529.                      *    p2 = end_pos + INTL_IsLeadByte(charset, string[end_pos]);
  3530.                      *    
  3531.                      *    p2 already point to the end of the selection
  3532.                      *    calling INTL_IsLeadByte(charset, string[end_pos]) is wrong here.
  3533.                      */
  3534.                         p2 = end_pos;
  3535.                         PA_UNLOCK(eptr->lo_text.text);
  3536.                         if (p2 > maxPos)
  3537.                         {
  3538.                             p2 = maxPos;
  3539.                         }
  3540.                     }
  3541.                     else
  3542.                     {
  3543.                         p2 = maxPos;
  3544.                     }
  3545.                     if (p2 < p1)
  3546.                     {
  3547.                         p2 = p1;
  3548.                     }
  3549.  
  3550.                 len = p2 - p1 + 1;
  3551.                 length += len;
  3552. #ifdef XP_WIN16
  3553.                 if (length > SIZE_LIMIT)
  3554.                 {
  3555.                     length -= len;
  3556.                     break;
  3557.                 }
  3558. #endif /* XP_WIN16 */
  3559.                 PA_LOCK(text, char *, eptr->lo_text.text);
  3560.                 XP_BCOPY((char *)(text + p1), tptr, len);
  3561.                 tptr = (char *)(tptr + len);
  3562.                 PA_UNLOCK(eptr->lo_text.text);
  3563.                 }
  3564.                 else
  3565.                 {
  3566.                 length += eptr->lo_text.text_len;
  3567. #ifdef XP_WIN16
  3568.                 if (length > SIZE_LIMIT)
  3569.                 {
  3570.                     length -= eptr->lo_text.text_len;
  3571.                     break;
  3572.                 }
  3573. #endif /* XP_WIN16 */
  3574.                 PA_LOCK(text, char *, eptr->lo_text.text);
  3575.                 XP_BCOPY(text, tptr, eptr->lo_text.text_len);
  3576.                 tptr = (char *)(tptr + eptr->lo_text.text_len);
  3577.                 PA_UNLOCK(eptr->lo_text.text);
  3578.                 }
  3579.             }
  3580.             break;
  3581.             case LO_LINEFEED:
  3582.             length += LINEBREAK_LEN;
  3583. #ifdef XP_WIN16
  3584.             if (length > SIZE_LIMIT)
  3585.             {
  3586.                 length -= LINEBREAK_LEN;
  3587.                 break;
  3588.             }
  3589. #endif /* XP_WIN16 */
  3590.             XP_BCOPY(LINEBREAK, tptr, LINEBREAK_LEN);
  3591.             tptr = (char *)(tptr + LINEBREAK_LEN);
  3592.             indent_counts = TRUE;
  3593.             break;
  3594.             case LO_HRULE:
  3595.             case LO_FORM_ELE:
  3596.             case LO_BULLET:
  3597.             case LO_IMAGE:
  3598.             case LO_SUBDOC:
  3599.             case LO_TABLE:
  3600.             default:
  3601.             break;
  3602.                 }
  3603.  
  3604.         last_id = eptr->lo_any.ele_id;
  3605.  
  3606.         /*
  3607.          * Jump cell boundries if there is one between start
  3608.          * and end.
  3609.          */
  3610.         if ((eptr->lo_any.next == NULL)&&(eptr != end))
  3611.         {
  3612.             eptr = lo_JumpCellWall(context, state, eptr);
  3613.         }
  3614.  
  3615.                 eptr = eptr->lo_any.next;
  3616.  
  3617.         /*
  3618.          * When we walk onto a cell, we need to walk into
  3619.          * it if it isn't empty.
  3620.          */
  3621.         if ((eptr != NULL)&&(eptr->type == LO_CELL)&&
  3622.             (eptr->lo_cell.cell_list != NULL))
  3623.         {
  3624.             eptr = eptr->lo_cell.cell_list;
  3625.         }
  3626.  
  3627.         /*
  3628.          * We don't want infinite loops.
  3629.          * If the element ID hasn't progressed, something
  3630.          * serious is wrong, and we should punt.
  3631.          */
  3632.         if ((eptr != NULL)&&(eptr->lo_any.ele_id <= last_id))
  3633.         {
  3634. #ifdef DEBUG
  3635. XP_TRACE(("Selection loop avoidance 4\n"));
  3636. #endif /* DEBUG */
  3637.             break;
  3638.         }
  3639.         }
  3640.     str[length] = '\0';
  3641.     length++;
  3642.     XP_UNLOCK_BLOCK(buff);
  3643.  
  3644.     return(buff);
  3645. }
  3646.  
  3647.  
  3648. XP_Block
  3649. LO_GetSelectionText(MWContext *context)
  3650. {
  3651.         int32 doc_id;
  3652.     lo_TopState *top_state;
  3653.         lo_DocState *state;
  3654.         LO_Element *start, *end;
  3655.     int32 start_pos, end_pos;
  3656.     XP_Block buff;
  3657.  
  3658.         /*
  3659.          * Get the unique document ID, and retreive this
  3660.          * documents layout state.
  3661.          */
  3662.         doc_id = XP_DOCID(context);
  3663.     top_state = lo_FetchTopState(doc_id);
  3664.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3665.         {
  3666.                 return(NULL);
  3667.         }
  3668.     state = top_state->doc_state;
  3669.  
  3670.         if ((state->selection_start == NULL)||(state->selection_end == NULL))
  3671.         {
  3672.                 return(NULL);
  3673.         }
  3674.  
  3675.         start = state->selection_start;
  3676.     start_pos = state->selection_start_pos;
  3677.         end = state->selection_end;
  3678.     end_pos = state->selection_end_pos;
  3679.  
  3680.     /* Make sure the selection is normalized */
  3681.     lo_NormalizeSelectionPoint(context, state, &start, &start_pos);
  3682.     lo_NormalizeSelectionEnd(context, state, &end, &end_pos);
  3683.  
  3684.     buff = lo_SelectionToText(context, state, start, start_pos,
  3685.             end, end_pos);
  3686.  
  3687.     return(buff);
  3688. }
  3689.  
  3690.  
  3691. Bool
  3692. LO_HaveSelection(MWContext *context)
  3693. {
  3694.         int32 doc_id;
  3695.     lo_TopState *top_state;
  3696.         lo_DocState *state;
  3697.  
  3698.         /*
  3699.          * Get the unique document ID, and retreive this
  3700.          * documents layout state.
  3701.          */
  3702.         doc_id = XP_DOCID(context);
  3703.     top_state = lo_FetchTopState(doc_id);
  3704.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3705.         {
  3706.                 return(FALSE);
  3707.         }
  3708.     state = top_state->doc_state;
  3709.  
  3710.         if ((state->selection_start == NULL)||(state->selection_end == NULL))
  3711.         {
  3712.                 return(FALSE);
  3713.         }
  3714.     else
  3715.         {
  3716.                 return(TRUE);
  3717.         }
  3718. }
  3719.  
  3720. /* return true if there is a current selection */
  3721.  
  3722. PRIVATE
  3723. Bool
  3724. lo_GetSelection(MWContext *context, LO_Selection* selection)
  3725. {
  3726.     int32 beginPosition;
  3727.     int32 endPosition;
  3728.     CL_Layer *sel_layer;
  3729.     /* LO_GetSelectionEndPoints uses int32. LO_Selection uses intn. So we need to
  3730.      * do the conversion here.
  3731.      */
  3732.  
  3733.     LO_GetSelectionEndpoints(context, &selection->begin.element, &selection->end.element,
  3734.         &beginPosition, &endPosition, &sel_layer);
  3735.     selection->begin.position = (intn) beginPosition;
  3736.     selection->end.position = (intn) endPosition;
  3737.     return selection->begin.element != NULL && selection->end.element != NULL;
  3738. }
  3739.  
  3740. void
  3741. LO_GetSelectionEndpoints(MWContext *context, LO_Element **start, LO_Element **end,
  3742.     int32 *start_pos, int32 *end_pos, CL_Layer **sel_layer)
  3743. {
  3744.         int32 doc_id;
  3745.     lo_TopState *top_state;
  3746.         lo_DocState *state;
  3747.  
  3748.     *start = NULL;
  3749.     *end = NULL;
  3750.     *start_pos = 0;
  3751.     *end_pos = 0;
  3752.         *sel_layer = NULL;
  3753.  
  3754.         /*
  3755.          * Get the unique document ID, and retreive this
  3756.          * documents layout state.
  3757.          */
  3758.         doc_id = XP_DOCID(context);
  3759.     top_state = lo_FetchTopState(doc_id);
  3760.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3761.         {
  3762.                 return;
  3763.         }
  3764.     state = top_state->doc_state;
  3765.  
  3766.         if ((state->selection_start == NULL)||(state->selection_end == NULL))
  3767.         {
  3768.                 return;
  3769.         }
  3770.  
  3771.         *start = state->selection_start;
  3772.     *start_pos = state->selection_start_pos;
  3773.         *end = state->selection_end;
  3774.     *end_pos = state->selection_end_pos;
  3775.         *sel_layer = state->selection_layer;
  3776. }
  3777.  
  3778. /* Returns TRUE if there was anything to select. If there is no data, or if
  3779.  * we're in editor mode and there is no editable data, then FALSE is returned.
  3780.  */
  3781.  
  3782. Bool
  3783. LO_SelectAll(MWContext *context)
  3784. {
  3785.     int32 doc_id;
  3786.     lo_TopState *top_state;
  3787.     lo_DocState *state;
  3788.     LO_Selection selection;
  3789.  
  3790.     /*
  3791.      * Get the unique document ID, and retreive this
  3792.      * documents layout state.
  3793.      */
  3794.     doc_id = XP_DOCID(context);
  3795.     top_state = lo_FetchTopState(doc_id);
  3796.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3797.     {
  3798.         return FALSE;
  3799.     }
  3800.     state = top_state->doc_state;
  3801.  
  3802.     LO_HighlightSelection(context, FALSE);
  3803.     selection.begin.element = NULL;
  3804.     selection.begin.position = 0;
  3805.     selection.end.element = NULL;
  3806.     selection.end.position = 0;
  3807.  
  3808.     if ( ! ( lo_FindDocumentEdge(context, state, &selection.begin, TRUE, FALSE)
  3809.         && lo_FindDocumentEdge(context, state, &selection.end, TRUE, TRUE) ) )
  3810.     {
  3811.         return FALSE;
  3812.     }
  3813. #if 0
  3814.     lo_ConvertInsertPointToSelectionEnd(context, state, &selection.end.element, &selection.end.position);
  3815. #endif
  3816.     LO_ASSERT_SELECTION(context, &selection);
  3817.     /*
  3818.      * Nothing to select.
  3819.      */
  3820.     if (selection.begin.element == NULL)
  3821.     {
  3822.         return FALSE;
  3823.     }
  3824.  
  3825.     state->selection_start = selection.begin.element;
  3826.     state->selection_start_pos = selection.begin.position;
  3827.     state->selection_end = selection.end.element;
  3828.     state->selection_end_pos = selection.end.position;
  3829.  
  3830. #if 0
  3831.     if ( ! lo_NormalizeSelection(context) ){
  3832.         return FALSE;
  3833.     }
  3834. #endif
  3835.     LO_HighlightSelection(context, TRUE);
  3836.     return TRUE;
  3837. }
  3838.  
  3839. #ifdef EDITOR 
  3840.  
  3841. /* This routine normalizes a selection so that it only selects editable elements.
  3842.  * It returns TRUE if the resulting selection is not empty.
  3843.  */
  3844. Bool
  3845. lo_NormalizeSelection(MWContext *context)
  3846. {
  3847.     Bool result;
  3848.     int32 doc_id;
  3849.     lo_TopState *top_state;
  3850.     lo_DocState *state;
  3851.     intn comparisonResult;
  3852.  
  3853. #ifdef DEBUG
  3854.     lo_VerifyLayout(context);
  3855. #endif /* DEBUG */
  3856.     result = FALSE;
  3857.     /*
  3858.      * Get the unique document ID, and retreive this
  3859.      * document's layout state.
  3860.      */
  3861.     doc_id = XP_DOCID(context);
  3862.     top_state = lo_FetchTopState(doc_id);
  3863.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  3864.     {
  3865.         return FALSE;
  3866.     }
  3867.     state = top_state->doc_state;
  3868.     if ( ! ( lo_EnsureEditableSearchNext2(context, state, &state->selection_start, & state->selection_start_pos)
  3869.         && lo_EnsureEditableSearchPrev2(context, state, &state->selection_end, & state->selection_end_pos) ) )
  3870.     {
  3871.         XP_ASSERT(FALSE);
  3872.         return FALSE;
  3873.     }
  3874.     /*
  3875.      * Ensure that the start position is before the end position
  3876.      */
  3877.     comparisonResult = lo_compare_selections(
  3878.         state->selection_start, state->selection_start_pos,
  3879.         state->selection_end, state->selection_end_pos );
  3880.     result = comparisonResult <= 0;
  3881.     if ( comparisonResult > 0 )
  3882.     {
  3883.         /*
  3884.          * It's not a legal selection. This can happen if we're editing and
  3885.          * the selected element is not editable. In this case we null out the selection
  3886.          */
  3887.          state->selection_start = NULL;
  3888.          state->selection_end = NULL;
  3889.          state->selection_start_pos = 0;
  3890.          state->selection_end_pos = 0;
  3891.     }
  3892.     return result;
  3893. }
  3894. #endif
  3895.  
  3896. Bool
  3897. lo_EnsureEditableSearchNext(MWContext *context, lo_DocState* state, LO_Element** eptr)
  3898. {
  3899.     int32 position = 0;
  3900.     return lo_EnsureEditableSearch2(context, state, eptr, &position, TRUE);
  3901. }
  3902.  
  3903. Bool
  3904. lo_EnsureEditableSearchNext2(MWContext *context, lo_DocState* state, LO_Element** eptr,
  3905.     int32* ePositionPtr)
  3906. {
  3907.     return lo_EnsureEditableSearch2(context, state, eptr, ePositionPtr, TRUE);
  3908. }
  3909.  
  3910. Bool
  3911. lo_EnsureEditableSearchPrev(MWContext *context, lo_DocState* state, LO_Element** eptr)
  3912. {
  3913.     int32 position = lo_GetMaximumInsertPointPosition(*eptr);
  3914.     return lo_EnsureEditableSearch2(context, state, eptr, &position, FALSE);
  3915. }
  3916.  
  3917. Bool
  3918. lo_EnsureEditableSearchPrev2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr)
  3919. {
  3920.     return lo_EnsureEditableSearch2(context, state, eptr, ePositionPtr, FALSE);
  3921. }
  3922.  
  3923. /* Returns TRUE if the result is editable. Doesn't change position if can't find editable. */
  3924.  
  3925. Bool
  3926. lo_EnsureEditableSearch(MWContext *context, lo_DocState* state, LO_Position* p, Bool forward)
  3927. {
  3928.     return lo_EnsureEditableSearch2(context, state, &p->element, &p->position, forward);
  3929. }
  3930.  
  3931. PRIVATE
  3932. Bool
  3933. lo_BumpToNextElement(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward)
  3934. {
  3935.     LO_Element* element = *eptr;
  3936.     int32 position = *ePositionPtr;
  3937.     if ( forward )
  3938.     {
  3939.         element = lo_BoundaryJumpingNext(context, state, element);
  3940.     }
  3941.     else
  3942.     {
  3943.         element = lo_BoundaryJumpingPrev(context, state, element);
  3944.     }
  3945.  
  3946.     if ( ! element ) return FALSE;
  3947.  
  3948.     if ( forward )
  3949.     {
  3950.         position = 0;
  3951.     }
  3952.     else
  3953.     {
  3954.         position = lo_GetMaximumInsertPointPosition(element);
  3955.     }
  3956.  
  3957.     *eptr = element;
  3958.     *ePositionPtr = position;
  3959.     return TRUE;
  3960. }
  3961.  
  3962. Bool
  3963. lo_EnsureEditableSearch2(MWContext *context, lo_DocState* state, LO_Element** eptr, int32* ePositionPtr, Bool forward)
  3964. {
  3965.     Bool moved = FALSE;
  3966.     LO_Element* element = *eptr;
  3967.     int32 position = *ePositionPtr;
  3968.  
  3969.     if ( ! element ) return FALSE;
  3970.  
  3971.     while ( ! lo_IsValidEditableInsertPoint2(context, state, element, position ) )
  3972.     {
  3973.         if ( ! lo_BumpToNextElement(context, state, &element, &position, forward ) )
  3974.         {
  3975.             return FALSE;
  3976.         }
  3977.         moved = TRUE;
  3978.     }
  3979.  
  3980.     if ( moved )
  3981.     {
  3982.         *eptr = element;
  3983.         *ePositionPtr = position;
  3984.     }
  3985.     return TRUE;
  3986. }
  3987.  
  3988. Bool
  3989. lo_IsValidEditableInsertPoint2(MWContext *context, lo_DocState* state, LO_Element* eptr, int32 position)
  3990. {
  3991.     return lo_ValidEditableElement(context, eptr)
  3992.         && position >= 0 && position <= lo_GetMaximumInsertPointPosition(eptr);
  3993. }
  3994.  
  3995. #ifdef EDITOR
  3996. /*
  3997.  *  This routine has too much knowledge of the layout engine.  Need to revisit
  3998. */
  3999. void LO_PositionCaret(MWContext *context, int32 x, int32 y, CL_Layer *layer)
  4000. {
  4001.     LO_Click(context, x, y, TRUE, layer);
  4002. }
  4003.  
  4004. void LO_DoubleClick(MWContext *context, int32 x, int32 y, CL_Layer *layer)
  4005. {
  4006.     int32 doc_id;
  4007.     lo_TopState *top_state;
  4008.     lo_DocState *state;
  4009.     LO_HitResult result;
  4010.  
  4011.     /*
  4012.      * Get the unique document ID, and retreive this
  4013.      * documents layout state.
  4014.      */
  4015.     doc_id = XP_DOCID(context);
  4016.     top_state = lo_FetchTopState(doc_id);
  4017.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  4018.     {
  4019.         return;
  4020.     }
  4021.     state = top_state->doc_state;
  4022.  
  4023.     LO_Hit(context, x, y, FALSE, &result, layer);
  4024.  
  4025.     lo_ProcessDoubleClick(context, top_state, state, &result, layer);
  4026. }
  4027.  
  4028.  
  4029. #endif
  4030.  
  4031. PRIVATE
  4032. void lo_GetHorizontalBounds(LO_Element* eptr, int32* returnBegin, int32* returnEnd)
  4033. {
  4034.     int32 width = eptr->lo_any.width;
  4035.     /*
  4036.      * Images need to account for border width
  4037.      */
  4038.     if (eptr->type == LO_IMAGE)
  4039.     {
  4040.         width = width + (2 * eptr->lo_image.border_width);
  4041.     }
  4042.     if (width <= 0)
  4043.     {
  4044.         width = 1;
  4045.     }
  4046.     *returnBegin = eptr->lo_any.x + eptr->lo_any.x_offset;
  4047.     *returnEnd = *returnBegin + width;
  4048. }
  4049.  
  4050. int32
  4051. lo_GetTextAttrMask(LO_Element* eptr)
  4052. {
  4053.     int32 mask = 0;
  4054.     LO_TextAttr* textAttr = 0;
  4055.     if ( ! eptr )
  4056.     {
  4057.         XP_ASSERT(FALSE);
  4058.         return mask;
  4059.     }
  4060.     switch ( eptr->type ) {
  4061.     case LO_TEXT:
  4062.        textAttr = eptr->lo_text.text_attr;
  4063.        break;
  4064.     case LO_IMAGE:
  4065.        textAttr = eptr->lo_image.text_attr;
  4066.        break;
  4067.     case LO_LINEFEED:
  4068.        textAttr = eptr->lo_linefeed.text_attr;
  4069.        break;
  4070.     default:
  4071.         break;
  4072.     }
  4073.     if ( textAttr ) {
  4074.         mask = textAttr->attrmask;
  4075.     }
  4076.     return mask;
  4077. }
  4078.  
  4079. int32
  4080. lo_GetElementLength(LO_Element* eptr)
  4081. {
  4082.     int32 length;
  4083.     if ( ! eptr )
  4084.     {
  4085.         XP_ASSERT(FALSE);
  4086.         return 1;
  4087.     }
  4088.     switch ( eptr->type ) {
  4089.     case LO_TEXT:
  4090.        length = eptr->lo_text.text_len;
  4091.        break;
  4092.     case LO_TEXTBLOCK:
  4093.     case LO_DESCTITLE:
  4094.         length = 0; /*MJUDGE 2-5-98*/
  4095.         break;
  4096.     case LO_HRULE:
  4097.     case LO_IMAGE:
  4098.     case LO_LINEFEED:
  4099.     default:
  4100.         length = 1;
  4101.         break;
  4102.     }
  4103.     return length;
  4104. }
  4105.  
  4106. LO_AnchorData*
  4107. lo_GetAnchorData(LO_Element* eptr)
  4108. {
  4109.     LO_AnchorData* result = 0;
  4110.     if ( ! eptr )
  4111.     {
  4112.         XP_ASSERT(FALSE);
  4113.         return result;
  4114.     }
  4115.     switch ( eptr->type ) {
  4116.     case LO_TEXT:
  4117.         result = eptr->lo_text.anchor_href;
  4118.         break;
  4119.     case LO_IMAGE:
  4120.         result = eptr->lo_image.anchor_href;
  4121.         break;
  4122.     case LO_LINEFEED:
  4123.         result = eptr->lo_linefeed.anchor_href;
  4124.         break;
  4125.    default:
  4126.         break;
  4127.     }
  4128.     return result;
  4129. }
  4130.  
  4131. LO_Element*
  4132. lo_GetNeighbor(LO_Element* element, Bool forward)
  4133. {
  4134.     LO_Element* result = NULL;
  4135.     if ( element ) {
  4136.         if ( forward )
  4137.             result = element->lo_any.next;
  4138.         else
  4139.             result = element->lo_any.prev;
  4140.     }
  4141.     return result;
  4142. }
  4143.  
  4144. int32
  4145. lo_GetElementEdge(LO_Element* element, Bool forward)
  4146. {
  4147.     int32 result = 0;
  4148.     if ( element ) {
  4149.         if ( forward )
  4150.             result = lo_GetElementLength(element);
  4151.     }
  4152.     return result;
  4153. }
  4154.  
  4155. int32
  4156. lo_GetMaximumInsertPointPosition(LO_Element* eptr)
  4157. {
  4158.     if ( ! eptr )
  4159.     {
  4160.         XP_ASSERT(FALSE);
  4161.         return 0;
  4162.     }
  4163. #if 0
  4164.     if ( eptr->type == LO_LINEFEED )
  4165.     {
  4166.         return 0;
  4167.     }
  4168.     else
  4169.     {
  4170.         return lo_GetElementLength(eptr);
  4171.     }
  4172. #endif
  4173.     return lo_GetElementLength(eptr);
  4174. }
  4175.  
  4176. int32
  4177. lo_IncrementPosition(LO_Element* eptr, int32 position)
  4178. {
  4179.    int32 length = lo_GetElementLength(eptr);
  4180.    if ( position < length )
  4181.    {
  4182.        if (eptr->type == LO_TEXT)
  4183.         {
  4184.             unsigned char *string;
  4185.  
  4186.             PA_LOCK(string, unsigned char *, eptr->lo_text.text);
  4187.             position = INTL_NextCharIdx(eptr->lo_text.text_attr->charset, 
  4188.                 string, position);
  4189.             PA_UNLOCK(eptr->lo_text.text);
  4190.         }
  4191.         else
  4192.             position++;
  4193.     }
  4194.     if ( position > length ) {
  4195.         position = length;
  4196.     }
  4197.     return position;
  4198. }
  4199.  
  4200. int32
  4201. lo_DecrementPosition(LO_Element* eptr, int32 position)
  4202. {
  4203.    if ( position > 0 )
  4204.    {
  4205.        if (eptr->type == LO_TEXT)
  4206.         {
  4207.             unsigned char *string;
  4208.  
  4209.             PA_LOCK(string, unsigned char *, eptr->lo_text.text);
  4210.             position = INTL_PrevCharIdx(eptr->lo_text.text_attr->charset, 
  4211.                 string, position);
  4212.             PA_UNLOCK(eptr->lo_text.text);
  4213.         }
  4214.         else
  4215.             position--;
  4216.     }
  4217.     if ( position < 0 ) {
  4218.         position = 0;
  4219.     }
  4220.     return position;
  4221. }
  4222.  
  4223. int32
  4224. lo_GetLastCharBeginPosition(LO_Element* eptr)
  4225. {
  4226.     return lo_DecrementPosition(eptr,
  4227.         lo_GetElementLength(eptr));
  4228. }
  4229.  
  4230. int32
  4231. lo_GetLastCharEndPosition(LO_Element* eptr)
  4232. {
  4233.     return lo_GetElementLength(eptr) - 1;
  4234. }
  4235.  
  4236. void lo_HitLine(MWContext *context, lo_DocState *state, int32 x, int32 y, Bool requireCaret,
  4237.                 LO_HitResult* result);
  4238. void lo_HitLine2(MWContext *context, lo_DocState *state, LO_Element* element, int32 position,
  4239.                  int32 x, LO_HitResult* result);
  4240. Bool lo_PositionIsOffEndOfLine(LO_HitElementResult* elementResult);
  4241.  
  4242. void lo_FullHitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
  4243.     Bool requireCaret,
  4244.     LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result);
  4245.  
  4246. PRIVATE
  4247. void lo_HitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
  4248.     Bool requireCaret,
  4249.     LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result)
  4250. {
  4251.     /* We hit something */
  4252.     result->type = LO_HIT_ELEMENT;
  4253.     result->lo_hitElement.position.element = eptr;
  4254.     switch ( eptr->lo_any.type )
  4255.         {
  4256.     case LO_TEXT:
  4257.         {
  4258.             int32 charStart;
  4259.             int32 charEnd;
  4260.             result->lo_hitElement.position.position = lo_ElementToCharOffset2(context, state, eptr, ret_x,
  4261.                 &charStart, &charEnd);
  4262.             if (result->lo_hitElement.position.position < 0)
  4263.             {
  4264.                 result->lo_hitElement.position.position = 0;
  4265.                 result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
  4266.             }
  4267.             else {
  4268.                 /*
  4269.                  * eptr points to the element 
  4270.                  * region is the part of the element
  4271.                 */
  4272.                 if( x > eptr->lo_any.x + eptr->lo_text.width )
  4273.                 {
  4274.                     result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
  4275.                 }
  4276.                 else
  4277.                 {
  4278.                     int32 charMiddle = (charStart + charEnd) / 2;
  4279.                     if ( ret_x < charMiddle ) {
  4280.                         result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
  4281.                     } else {
  4282.                         result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
  4283.                     }
  4284.                 }
  4285.             }
  4286.         }
  4287.         break;
  4288.     default:
  4289.         {
  4290.             int32 begin;
  4291.             int32 end;
  4292.             result->lo_hitElement.position.position = 0;
  4293.             lo_GetHorizontalBounds(eptr, &begin, &end);
  4294.             if ( requireCaret )
  4295.             {
  4296.                 int32 middle = (begin + end) / 2;
  4297.                 result->lo_hitElement.region =
  4298.                     ( x < middle ) ? LO_HIT_ELEMENT_REGION_BEFORE
  4299.                         : LO_HIT_ELEMENT_REGION_AFTER;
  4300.             }
  4301.             else
  4302.             {
  4303.                 int32 midBegin;
  4304.                 int32 midEnd;
  4305.                 midBegin = begin + 3;
  4306.                 midEnd = end - 3;
  4307.                 if ( midEnd - midBegin < 6 ) {
  4308.                     if ( end - begin <= 6 ) {
  4309.                         /* Very narrow picture. 0..6 pixels */
  4310.                         midBegin = begin;
  4311.                         midEnd = end;
  4312.                     } else {
  4313.                         /* Narrow picture. 6+..12 pixels */
  4314.                         int32 margin = end - begin - 6;
  4315.                         int32 halfMargin = margin / 2;
  4316.                         midBegin = begin + halfMargin;
  4317.                         midEnd = end - (margin - halfMargin);
  4318.                     }
  4319.                 }
  4320.                 if ( x < midBegin ) {
  4321.                     result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_BEFORE;
  4322.                 } else if ( x < midEnd ) {
  4323.                     result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_MIDDLE;
  4324.                 } else {
  4325.                     result->lo_hitElement.region = LO_HIT_ELEMENT_REGION_AFTER;
  4326.                 }
  4327.             }
  4328.         }
  4329.         break;
  4330.     }
  4331.  
  4332. }
  4333.  
  4334. void lo_FullHitElement(MWContext *context, lo_DocState* state, int32 x, int32 y,
  4335.     Bool requireCaret,
  4336.     LO_Element* eptr, int32 ret_x, int32 ret_y, LO_HitResult* result)
  4337. {
  4338.     if ( eptr->type != LO_LINEFEED ) {
  4339.         /* Seek forward to find an editable element */
  4340.         if ( ! lo_EnsureEditableSearchNext(context, state, &eptr) )
  4341.         {
  4342.             lo_EnsureEditableSearchPrev(context, state, &eptr);
  4343.         }
  4344.         lo_HitElement(context, state, x, y, requireCaret, eptr, ret_x, ret_y, result);
  4345.         /* Check if we ran off the end of the line */
  4346.         if ( result->type == LO_HIT_UNKNOWN )
  4347.  
  4348. #if 0 /* leads to infinite recursion  when draging around tables. */
  4349.         if ( result->type == LO_HIT_UNKNOWN 
  4350.             || result->type == LO_HIT_ELEMENT
  4351.                 && lo_PositionIsOffEndOfLine(& result->lo_hitElement) )
  4352. #endif
  4353.  
  4354.         {
  4355.             /* XP_TRACE(("Element off end of line.")); */
  4356.             lo_HitLine(context, state, x, y, requireCaret, result);
  4357.         }
  4358.     }
  4359.     else
  4360.     {
  4361.         /* There's a bug selecting the last character of text in a table
  4362.          * that bites us if we use lo_HitLine. So rather than starting the
  4363.          * search from the beginning, we start from the linefeed.
  4364.          */
  4365.         lo_HitLine2(context, state, eptr, 0, x, result);
  4366.     }
  4367. }
  4368.  
  4369. PRIVATE
  4370. void lo_HitCellWideMatch(MWContext *context, lo_DocState *state, LO_CellStruct* cellPtr, int32 x, int32 y, Bool requireCaret, LO_HitResult* result)
  4371. {
  4372. #if 0
  4373.     LO_Element* eptr = lo_XYToNearestCellElement(context, state, cellPtr, x, y);
  4374. #endif
  4375.     /*
  4376.      * The last argument is FALSE so that we search upwards. This helps us in tables
  4377.      * in the editor.
  4378.      */
  4379.  
  4380.     LO_Element* eptr = lo_search_element_list_WideMatch(context, cellPtr->cell_list, NULL, x, y, FALSE);
  4381.     if ( eptr )
  4382.     {
  4383.         lo_FullHitElement(context, state, x, y, requireCaret, eptr, x, y, result);
  4384.     }
  4385. }
  4386.  
  4387. PRIVATE
  4388. void lo_HitCell(MWContext *context, lo_DocState *state, LO_CellStruct* cellPtr, int32 x, int32 y, Bool requireCaret, LO_HitResult* result)
  4389. {
  4390.     LO_Element* eptr = lo_XYToNearestCellElement(context, state, cellPtr, x, y);
  4391.     if ( eptr )
  4392.     {
  4393.         lo_FullHitElement(context, state, x, y, requireCaret, eptr, x, y, result);
  4394.     }
  4395. }
  4396.  
  4397. void lo_HitLine(MWContext *context, lo_DocState *state, int32 x, int32 y, Bool requireCaret,
  4398.                 LO_HitResult* result)
  4399. {
  4400.     int32 line;
  4401.     result->type = LO_HIT_UNKNOWN;
  4402.     
  4403.     /*
  4404.      * Search from current line backwards to find something to edit.
  4405.      */
  4406.     for ( line = lo_PointToLine(context, state, x, y);
  4407.         line >= 0;
  4408.         line-- )
  4409.     {
  4410.         LO_Element* begin;
  4411.         LO_Element* end;
  4412.         LO_Element* tptr;
  4413.         lo_GetLineEnds(context, state, line, & begin, & end);
  4414.         /* lo_GetLineEnds returns the start of the next line for 'end' */
  4415.         if ( end ) {
  4416.             end = end->lo_any.prev;
  4417.         } else {
  4418.             /* Last line. We know that the last line only has one element. */
  4419.             end = begin;
  4420.         }
  4421.         /* Except for cases where the entire line is a line feed, don't select the end line-feed. */
  4422.         if ( begin->type != LO_LINEFEED && end->type == LO_LINEFEED ) {
  4423.             end = end->lo_any.prev;
  4424.         }
  4425.  
  4426.         if ( begin->type == LO_TABLE )
  4427.         {
  4428.             /* Search inside the table to find which cell/caption we hit */
  4429.             LO_Element* tptr;
  4430.             tptr = begin->lo_any.next;
  4431.             while ((tptr != NULL)&&(tptr->type == LO_CELL))
  4432.             {
  4433.                 if ( tptr->lo_any.x <= x &&
  4434.                     x < tptr->lo_any.x + tptr->lo_any.width &&
  4435.                     tptr->lo_any.y <= y &&
  4436.                     y < tptr->lo_any.y + tptr->lo_any.height ) {
  4437.                     /* We hit this cell */
  4438.                     /* lo_HitCellWideMatch(context, state, (LO_CellStruct*) tptr, x, y, requireCaret, result); */
  4439.                     /* Replacing call to lo_HitCellWideMatch with lo_HitCell because the former function does
  4440.                        not drill down into cell lists to find the closest document element to x,y.  This fixes
  4441.                        selection in tables.  */
  4442.                     lo_HitCell(context, state, (LO_CellStruct*) tptr, x, y, requireCaret, result);
  4443.                     break;
  4444.                 }
  4445.                 tptr = tptr->lo_any.next;
  4446.             }
  4447.             return;
  4448.         }
  4449.  
  4450.         /* 
  4451.          * Loop through the elements in the line and, if any are inflow
  4452.          * layers, go into them. We bail out if we've reached the end of
  4453.          * the line (or null for the last line).
  4454.          */
  4455.         for( tptr = begin; tptr && ((tptr != end) || (begin == end)); 
  4456.              tptr = tptr->lo_any.next) {
  4457.             if ( tptr->type == LO_CELL && tptr->lo_cell.cell_inflow_layer &&
  4458.                  tptr->lo_any.x <= x &&
  4459.                  x < tptr->lo_any.x + tptr->lo_any.width &&
  4460.                  tptr->lo_any.y <= y &&
  4461.                  y < tptr->lo_any.y + tptr->lo_any.height ) {
  4462.                 lo_HitCell(context, state, (LO_CellStruct *)tptr,
  4463.                            x, y, requireCaret, result);
  4464.                 return;
  4465.             }
  4466.         }
  4467.         
  4468.         /* Make the end-points editable */
  4469.         if ( ! lo_EnsureEditableSearchNext(context, state, &begin) )
  4470.             continue;
  4471.         if ( ! lo_EnsureEditableSearchPrev(context, state, &end) )
  4472.             return;
  4473.         if ( begin && end && begin->lo_any.ele_id <= end->lo_any.ele_id )
  4474.         {
  4475.             result->type = LO_HIT_LINE;
  4476.             if ( x < begin->lo_any.x ) {
  4477.                 result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
  4478.             } else {
  4479.                 result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
  4480.             }
  4481.             result->lo_hitLine.selection.begin.element = begin;
  4482.             result->lo_hitLine.selection.begin.position = 0;
  4483.             result->lo_hitLine.selection.end.element = end;
  4484.             if ( end->type == LO_LINEFEED )
  4485.             {
  4486.                 result->lo_hitLine.selection.end.position = 0;
  4487.             }
  4488.             else
  4489.             {
  4490.                 result->lo_hitLine.selection.end.position = lo_GetMaximumInsertPointPosition(end);
  4491.             }
  4492. #if 0
  4493.             XP_TRACE(("b %d e %d\n", result->lo_hitLine.begin->lo_any.ele_id,
  4494.                  result->lo_hitLine.end->lo_any.ele_id));
  4495. #endif
  4496.             break;
  4497.         }
  4498.     }
  4499. }
  4500.  
  4501.  
  4502. #ifdef MQUOTE
  4503. /* Returns true if the only elements between begin and end are bullets, not including endpoints. */
  4504. Bool lo_OnlyBulletsBetween(LO_Element *begin,LO_Element *end)
  4505. {
  4506.     /* Both begin and end should exist, and begin should be strictly before end. */
  4507.     if (!begin || !end || (begin->lo_any.ele_id >= end->lo_any.ele_id)) 
  4508.     {
  4509.         XP_ASSERT(FALSE);
  4510.         return FALSE;
  4511.     }
  4512.  
  4513.     /* We should never get into an infinite loop because we made sure begin was before end. */
  4514.     do {
  4515.         if (begin->lo_any.next == end)
  4516.         {
  4517.             return TRUE;
  4518.         }
  4519.  
  4520.         begin = begin->lo_any.next;
  4521.     } while (begin->type == LO_BULLET);
  4522.  
  4523.     /* Hit something that's not a bullet between begin and end. */
  4524.     return FALSE;
  4525. }
  4526. #endif
  4527.  
  4528. void lo_HitLine2(MWContext *context, lo_DocState *state, LO_Element* element,
  4529.                  int32 position, int32 x, LO_HitResult* result)
  4530. {
  4531.     LO_Element* begin;
  4532.     LO_Element* end;
  4533.  
  4534.     result->type = LO_HIT_UNKNOWN;
  4535.  
  4536.     end = element;
  4537.  
  4538.     for(;;)
  4539.     {
  4540.         /* If this is a non-editable line feed, go backwards to the previous editable line. */
  4541.         while  ( end && end->type == LO_LINEFEED
  4542.             && end->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT)
  4543.         {
  4544.             end = end->lo_any.prev;
  4545.         }
  4546.         if ( ! end )
  4547.             return;
  4548.  
  4549.         /* Search forward to find the end of line */
  4550.         for ( ;
  4551.             end;
  4552.             end = end->lo_any.next)
  4553.         {
  4554.             if ( end->type == LO_LINEFEED ) break;
  4555.         }
  4556.  
  4557.         if ( ! end )
  4558.         {
  4559.             return;
  4560.         }
  4561.        /* Search backwards to find the beginning of the line. */
  4562.         for ( begin = end->lo_any.prev;
  4563.             begin;
  4564.             begin = begin->lo_any.prev)
  4565.         {
  4566.             if ( begin->type == LO_LINEFEED )
  4567.             {
  4568. #ifdef MQUOTE
  4569.                 /* We have the case of an editable linefeed on a line by itself when the only
  4570.                    thing on the line are bullets.  I.e. from a <mquote> tag. */
  4571.                 if ( lo_OnlyBulletsBetween(begin,end) )
  4572. #else
  4573.                 if ( begin->lo_any.next == end )
  4574. #endif
  4575.                 {
  4576.                     /* editable linefeed on a line by itself. */
  4577.                     result->type = LO_HIT_LINE;
  4578.                     if ( x < end->lo_any.x ) {
  4579.                         result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
  4580.                     } else {
  4581.                         result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
  4582.                     }
  4583.                     result->lo_hitLine.selection.begin.element = end;
  4584.                     result->lo_hitLine.selection.begin.position = 0;
  4585.                     result->lo_hitLine.selection.end = result->lo_hitLine.selection.begin;
  4586.                     return;
  4587.                 }
  4588.                 begin = begin->lo_any.next;
  4589.                 break;
  4590.             }
  4591.             if ( ! begin->lo_any.prev )
  4592.                 break;  /* Start of document */
  4593.         }
  4594.     
  4595.         if ( ! begin )
  4596.         {
  4597.             /* Must be a line-feed at the beginning of the document */
  4598.             begin = end;
  4599.         }
  4600.  
  4601.     
  4602.         /* Except for cases where the entire line is a line feed, don't select the end line-feed. */
  4603.         if ( begin->type != LO_LINEFEED && end->type == LO_LINEFEED ) {
  4604.             end = end->lo_any.prev;
  4605.         }
  4606.  
  4607.         if ( ( begin->type == LO_TABLE ) || 
  4608.              (begin->type == LO_CELL && begin->lo_cell.cell_inflow_layer) )
  4609.         {
  4610.             /* If this is a table or an inflow layer, give up. 
  4611.                We don't understand them, yet. */
  4612.             return;
  4613.         }
  4614.  
  4615.         /* Make the end-points editable */
  4616.         if ( ! lo_EnsureEditableSearchNext(context, state, & begin) ) {
  4617.             /* This is the last, unselectable line of a document in edit mode.
  4618.              * Select the previous line.
  4619.              */
  4620.             if ( lo_EnsureEditableSearchPrev(context, state, & begin) ){
  4621.                 lo_HitLine2(context, state, begin, 0, 0, result);
  4622.             }
  4623.             return;
  4624.         }
  4625.         if ( ! lo_EnsureEditableSearchPrev(context, state, & end) )
  4626.             return;
  4627.     
  4628.         if ( begin && end && begin->lo_any.ele_id <= end->lo_any.ele_id )
  4629.         {
  4630.             /* This is a good line */
  4631.             break;
  4632.         }
  4633.         /* At this point "end" points to the previous editable line. So try again. */
  4634.     }
  4635.  
  4636.     result->type = LO_HIT_LINE;
  4637.     if ( x < begin->lo_any.x ) {
  4638.         result->lo_hitLine.region = LO_HIT_LINE_REGION_BEFORE;
  4639.     } else {
  4640.         result->lo_hitLine.region = LO_HIT_LINE_REGION_AFTER;
  4641.     }
  4642.     result->lo_hitLine.selection.begin.element = begin;
  4643.     result->lo_hitLine.selection.begin.position = 0;
  4644.     result->lo_hitLine.selection.end.element = end;
  4645.     if ( end->type == LO_LINEFEED )
  4646.     {
  4647.         result->lo_hitLine.selection.end.position = 0;
  4648.     }
  4649.     else
  4650.     {
  4651.         result->lo_hitLine.selection.end.position = lo_GetMaximumInsertPointPosition(end);
  4652.     }
  4653. }
  4654.  
  4655.  
  4656. Bool
  4657. lo_PositionIsOffEndOfLine(LO_HitElementResult* elementResult)
  4658. {
  4659.     if ( elementResult->region == LO_HIT_ELEMENT_REGION_AFTER )
  4660.     {
  4661.         LO_Element* eptr = elementResult->position.element;
  4662.         int32 position = elementResult->position.position;
  4663.         if ( eptr && eptr->lo_any.next && eptr->lo_any.next->type == LO_LINEFEED )
  4664.         {
  4665.             return position >= lo_GetLastCharEndPosition(eptr);
  4666.         }
  4667.     }
  4668.     return FALSE;
  4669. }
  4670.  
  4671. /*
  4672.  * LO_Hit
  4673.  * Determines what semantic part of the layout was hit for a given x/y position. 
  4674.  * This is intended to be called by cursor tracking, drag & drop, etc.
  4675.  *
  4676.  */
  4677.  
  4678. void LO_Hit(MWContext *context, int32 x, int32 y, Bool requireCaret,
  4679.     LO_HitResult* result, CL_Layer *layer)
  4680. {
  4681.     int32 doc_id;
  4682.     lo_TopState *top_state;
  4683.     lo_DocState *state;
  4684.     LO_Element *eptr;
  4685.     int32 position;
  4686.     int32 ret_x, ret_y;
  4687.     LO_CellStruct *layer_cell;
  4688.  
  4689. #if 0
  4690.     lo_PrintLayout(context);
  4691. #endif /* DEBUG */
  4692.  
  4693.     result->type = LO_HIT_UNKNOWN;
  4694.     /*
  4695.      * Get the unique document ID, and retreive this
  4696.      * documents layout state.
  4697.      */
  4698.     doc_id = XP_DOCID(context);
  4699.     top_state = lo_FetchTopState(doc_id);
  4700.     if ((top_state == NULL)||(top_state->doc_state == NULL)) {
  4701.         return;
  4702.     }
  4703.     state = top_state->doc_state;
  4704.  
  4705.     layer_cell = lo_GetCellFromLayer(context, layer);
  4706.     if (layer_cell != NULL) {
  4707.         lo_HitCell(context,
  4708.                    state,
  4709.                    layer_cell,
  4710.                    x, y, requireCaret, result);
  4711.     }
  4712.     else {
  4713.  
  4714.     /* Clip Y to the last line of the document */
  4715.     {
  4716.         LO_Position endOfDocument;
  4717.         int32 endY;
  4718.     LO_Element *last_eptr;
  4719.     last_eptr = LO_getFirstLastElement(context, FALSE);
  4720.         if (last_eptr == NULL)
  4721.         {
  4722.             return;
  4723.         }
  4724.         endY = last_eptr->lo_any.y
  4725.             + last_eptr->lo_any.y_offset
  4726.             + last_eptr->lo_any.height;
  4727.         if ( y >= endY ){
  4728.             y = endY - 1;
  4729.         }
  4730.     }
  4731.  
  4732.     /* Clip Y to the first line of the document */
  4733.     {
  4734.         LO_Position startOfDocument;
  4735.         int32 startY;
  4736.     LO_Element *first_eptr;
  4737.     first_eptr = LO_getFirstLastElement(context, TRUE);
  4738.         if (first_eptr == NULL)
  4739.         {
  4740.             return;
  4741.         }
  4742.         /* Curiously, tables have an offset which excludes their captions. So don't
  4743.          * add in the offset, or else you won't be able to select the caption of a
  4744.          * table at the start of a document.
  4745.          */
  4746.         startY = first_eptr->lo_any.y;
  4747.         if ( y < startY ){
  4748.             y = startY;
  4749.         }
  4750.     }
  4751.  
  4752.     /* Note: Setting the first Boolean to TRUE allows you to hit-select into floating elements,
  4753.      * which basicly means into floating tables. Unfortunately, floating element ids are not numbered
  4754.      * consecutively with respect to the main document flow, which means that selections that
  4755.      * cross from below the floating element into the floating element will compare and draw wrong.
  4756.      */
  4757.  
  4758.     position = 0;
  4759.     eptr = lo_XYToDocumentElement2(context, state, x, y, FALSE, TRUE, TRUE, 
  4760.         TRUE, &ret_x, &ret_y);
  4761.  
  4762.     /* LO_DUMP_INSERT_POINT("lo_XYToDocumentElement2", eptr, 0); */
  4763.  
  4764.     if ( eptr ) {
  4765.         lo_FullHitElement(context, state, x, y, requireCaret, eptr, ret_x, ret_y, result);
  4766.     }
  4767.     else {
  4768.         lo_HitLine(context, state, x, y, requireCaret, result);
  4769.    }
  4770.  
  4771.     }
  4772.  
  4773. #ifdef DEBUG
  4774.     {
  4775. /*        const char* kTypes[] = { "LO_HIT_UNKNOWN", "LO_HIT_LINE", "LO_HIT_ELEMENT"};*/
  4776.         XP_ASSERT ( result->type >= LO_HIT_UNKNOWN &&  result->type <= LO_HIT_ELEMENT );
  4777.     }
  4778.  
  4779. #if 0
  4780.     /* Useful for debugging mouse selection, */ 
  4781.     LO_DUMP_HIT_RESULT(result);
  4782. #endif
  4783.  
  4784. #endif
  4785. }
  4786.  
  4787. void
  4788. lo_SetInsertPoint(MWContext *context, lo_TopState *top_state, LO_Element* eptr, int32 position, CL_Layer *layer)
  4789. {
  4790.     if ( EDT_IS_EDITOR(context) )
  4791.     {
  4792. #ifdef EDITOR
  4793.         LO_ASSERT_POSITION2(context, eptr, position);
  4794.         if ( ! lo_IsValidEditableInsertPoint2(context, top_state->doc_state, eptr, position) )
  4795.         {
  4796.             XP_ASSERT(FALSE);
  4797.         }
  4798.         else
  4799.         {
  4800.             EDT_SetInsertPoint( top_state->edit_buffer,
  4801.                 eptr->lo_any.edit_element, 
  4802.                 eptr->lo_any.edit_offset + position,
  4803.                 position == 0);
  4804.         }
  4805. #endif
  4806.     }
  4807.     LO_StartSelectionFromElement( context, eptr, position, layer );
  4808. }
  4809.  
  4810. /*
  4811.  * Like LO_PositionCaret, except it selects non-text items as well.
  4812.  * Returns TRUE if the result is a selection, FALSE if the result is
  4813.  * an insertion point. (The layout level
  4814.  * does not understand the concept of insertion point.) 
  4815.  */
  4816.  
  4817. Bool LO_Click(MWContext *context, int32 x, int32 y, Bool requireCaret,
  4818.               CL_Layer *layer)
  4819. {
  4820.     int32 doc_id;
  4821.     lo_TopState *top_state;
  4822.     lo_DocState *state;
  4823.     LO_HitResult result;
  4824.  
  4825.     /*
  4826.      * Get the unique document ID, and retreive this
  4827.      * documents layout state.
  4828.      */
  4829.     doc_id = XP_DOCID(context);
  4830.     top_state = lo_FetchTopState(doc_id);
  4831.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  4832.     {
  4833.         return FALSE;
  4834.     }
  4835.     state = top_state->doc_state;
  4836.  
  4837.     LO_Hit(context, x, y, requireCaret, &result, layer);
  4838.  
  4839.     return lo_ProcessClick(context, top_state, state, &result, requireCaret,
  4840.                            layer);
  4841. }
  4842.  
  4843. Bool lo_ProcessClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, Bool requireCaret, CL_Layer *layer)
  4844. {
  4845.     switch ( result->type )
  4846.     {
  4847.     case LO_HIT_LINE:
  4848.         {
  4849.             switch ( result->lo_hitLine.region )
  4850.             {
  4851.             case LO_HIT_LINE_REGION_BEFORE:
  4852.                 {
  4853.                     if ( requireCaret )
  4854.                     /* Insertion point before first element of line */
  4855.                     {
  4856.                          LO_Element* eptr = result->lo_hitLine.selection.begin.element;
  4857.                         int32 position = result->lo_hitLine.selection.begin.position;
  4858.                         lo_SetInsertPoint(context, top_state, eptr, position, layer);
  4859.                         return FALSE;
  4860.                     }
  4861.                     else
  4862.                     {
  4863.                         /* Select the line */
  4864.                         lo_ExtendToIncludeHardBreak(context, state, & result->lo_hitLine.selection);
  4865.                         lo_SetSelection(context, & result->lo_hitLine.selection, FALSE);
  4866.                         return TRUE;
  4867.                     }
  4868.                }
  4869.                 break;
  4870.             case LO_HIT_LINE_REGION_AFTER:
  4871.                 {
  4872.                     /* Set the insertion point after the last element of line */
  4873.                      LO_Element* eptr = result->lo_hitLine.selection.end.element;
  4874.                     int32 position = result->lo_hitLine.selection.end.position;
  4875.                     lo_EnsureEditableSearchPrev2(context, state, &eptr, &position);
  4876.                     lo_SetInsertPoint(context, top_state, eptr, 
  4877.                                       position, layer);
  4878.                     return FALSE;
  4879.                }
  4880.                 break;
  4881.             default:
  4882.                 break;
  4883.             }
  4884.         }
  4885.         break;
  4886.     case LO_HIT_ELEMENT:
  4887.         {
  4888.             LO_Element* eptr = result->lo_hitElement.position.element;
  4889.             int32 position  = result->lo_hitElement.position.position;
  4890.             switch ( result->lo_hitElement.region )
  4891.             {
  4892.             case LO_HIT_ELEMENT_REGION_BEFORE:
  4893.                 {
  4894.                     lo_SetInsertPoint(context, top_state, eptr, position, 
  4895.                                       layer);
  4896.                     return FALSE;
  4897.                 }
  4898.                 break;
  4899.             case LO_HIT_ELEMENT_REGION_MIDDLE:
  4900.                 {
  4901.                     /* Select the element */
  4902.                     LO_StartSelectionFromElement( context, eptr, position, 
  4903.                                                   layer );
  4904.                     lo_BumpEditablePositionForward(context, state, &eptr, &position);
  4905.                     LO_ExtendSelectionFromElement( context, eptr, position, FALSE );
  4906.                     LO_HighlightSelection(context, TRUE);
  4907.                     return TRUE;
  4908.                 }
  4909.                 break;
  4910.             case LO_HIT_ELEMENT_REGION_AFTER:
  4911.                 {
  4912.                     lo_BumpEditablePositionForward(context, state,
  4913.                         &eptr, &position);
  4914.                     lo_SetInsertPoint(context, top_state, eptr, 
  4915.                                       position, layer);
  4916.                     return FALSE;
  4917.                 }
  4918.                 break;
  4919.             default:
  4920.                 break;
  4921.             }
  4922.         }
  4923.         break;
  4924.     default:
  4925.         break;
  4926.     }
  4927.     return FALSE;
  4928. }
  4929.  
  4930. /* Shares much functionality with lo_ProcessClick
  4931.  * If any changes made in logic above, please check
  4932.  *  this and make corresponding changes if relevant
  4933.  *  Returns the element that we will drop at and position 
  4934.  *   within this element for text data
  4935. */
  4936. LO_Element * lo_PositionDropCaret(MWContext *pContext, int32 x, int32 y, int32 * pPosition) 
  4937. {
  4938.     LO_Element* eptr = NULL;
  4939. #ifdef EDITOR
  4940.     lo_TopState *top_state = lo_FetchTopState(XP_DOCID(pContext));
  4941.     int32 position;
  4942.     LO_HitResult result;
  4943.     lo_DocState *state;
  4944.  
  4945.     if ((top_state == NULL)||(top_state->doc_state == NULL)) {
  4946.         return NULL;
  4947.     }
  4948.     state =  top_state->doc_state;
  4949.  
  4950.     LO_Hit(pContext, x, y, FALSE /*requireCaret*/, &result, 0);
  4951.  
  4952.     /* This was copied from lo_ProcessClick above
  4953.      * We want to execute most of the same logic to locate the caret position
  4954.      *  without calling lo_SetInsertPoint, which sets the regular caret
  4955.      *  and is incompatable with a selection
  4956.     */
  4957.  
  4958.     switch ( result.type )
  4959.     {
  4960.         case LO_HIT_LINE:
  4961.             switch ( result.lo_hitLine.region )
  4962.             {
  4963.                 case LO_HIT_LINE_REGION_BEFORE:
  4964.                     /* Drop point before first element of line */
  4965.                     eptr = result.lo_hitLine.selection.begin.element;
  4966.                     position = result.lo_hitLine.selection.begin.position;
  4967.                     break;
  4968.                 case LO_HIT_LINE_REGION_AFTER:
  4969.                     /* Set the drop point after the last element of line */
  4970.                      eptr = result.lo_hitLine.selection.end.element;
  4971.                     position = result.lo_hitLine.selection.end.position;
  4972.                     lo_EnsureEditableSearchPrev2(pContext, state, &eptr, &position);
  4973.                     break;
  4974.                 default:
  4975.                     break;
  4976.             }
  4977.             break;
  4978.         case LO_HIT_ELEMENT:
  4979.             eptr = result.lo_hitElement.position.element;
  4980.             position  = result.lo_hitElement.position.position;
  4981.             
  4982.             /* Move to just past the element if after it */
  4983.             if( result.lo_hitElement.region == LO_HIT_ELEMENT_REGION_AFTER ){
  4984.                 lo_BumpEditablePositionForward(pContext, state, &eptr, &position);
  4985.             }
  4986.             break;
  4987.         default:
  4988.             break;
  4989.     }
  4990.  
  4991.     if( eptr )
  4992.     {
  4993.         LO_ASSERT_POSITION2(pContext, eptr, position);
  4994.         if ( lo_IsValidEditableInsertPoint2(pContext, state, eptr, position) )
  4995.         {
  4996.             if( EDT_IsSelected(pContext) )
  4997.             {
  4998.                 FE_DestroyCaret(pContext);
  4999.             }
  5000.  
  5001.             switch ( eptr->type )
  5002.             {
  5003.                 case LO_TEXT:
  5004.                     FE_DisplayTextCaret( pContext, FE_VIEW,
  5005.                                 &eptr->lo_text,
  5006.                                 (ED_CaretObjectPosition)position );
  5007.                     break;
  5008.                 case LO_IMAGE:
  5009.                     FE_DisplayImageCaret( pContext,
  5010.                                 &eptr->lo_image,
  5011.                                 (ED_CaretObjectPosition)position );
  5012.                     break;
  5013.                 default:
  5014.                     FE_DisplayGenericCaret( pContext,
  5015.                                 &eptr->lo_any,
  5016.                                 (ED_CaretObjectPosition)position );
  5017.                     break;
  5018.             }
  5019.         }
  5020.         else
  5021.         {
  5022.             XP_ASSERT(FALSE);
  5023.         }
  5024.     }
  5025.     if( pPosition ){
  5026.         *pPosition = position;
  5027.     }
  5028. #endif /* EDITOR */
  5029.     return eptr;
  5030. }
  5031.  
  5032.  
  5033. /* Returns TRUE if the result is an anchor. Also selects the entire anchor. */
  5034. Bool lo_ProcessAnchorClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result)
  5035. {
  5036.     switch ( result->type )
  5037.     {
  5038.     case LO_HIT_LINE:
  5039.         {
  5040.             return FALSE;
  5041.         }
  5042.         break;
  5043.     case LO_HIT_ELEMENT:
  5044.         {
  5045.             LO_Element* eptr = result->lo_hitElement.position.element;
  5046.             switch ( result->lo_hitElement.region )
  5047.             {
  5048.             case LO_HIT_ELEMENT_REGION_BEFORE:
  5049.             case LO_HIT_ELEMENT_REGION_AFTER:
  5050.                 {
  5051.                     return lo_SelectAnchor(context, state, eptr);
  5052.                 }
  5053.                 break;
  5054.             case LO_HIT_ELEMENT_REGION_MIDDLE:
  5055.                 {
  5056.                     return FALSE;
  5057.                 }
  5058.                 break;
  5059.             default:
  5060.                 break;
  5061.             }
  5062.         }
  5063.         break;
  5064.     default:
  5065.         break;
  5066.     }
  5067.     return FALSE;
  5068. }
  5069.  
  5070. PRIVATE
  5071. void
  5072. lo_FindStartOfParagraph(MWContext* context, lo_DocState * state, LO_Position* where, LO_Position* paragraphStart)
  5073. {
  5074.     /*
  5075.      * Search backwards to find the beginning of the paragraph. A paragraph starts
  5076.      * with the beginning of the document, or after a forced break.
  5077.      */
  5078.     LO_Element* element;
  5079.     if ( ! (where && where->element ) )
  5080.     {
  5081.         XP_ASSERT(FALSE);
  5082.         return;
  5083.     }
  5084.     
  5085.     element = where->element;
  5086.  
  5087.     for(;;)
  5088.     {
  5089.         LO_Element* prev = element->lo_any.prev;
  5090.         if ( ! prev ) break;
  5091.         if ( lo_IsEndOfParagraph2(context, prev, 0) )
  5092.         {
  5093.             break;
  5094.         }
  5095.         element = prev;
  5096.     }
  5097.  
  5098.     paragraphStart->element = element;
  5099.     paragraphStart->position = 0;
  5100.     lo_EnsureEditableSearchNext2(context, state, ¶graphStart->element, & paragraphStart->position);
  5101. }
  5102.  
  5103. PRIVATE
  5104. void
  5105. lo_FindEndOfParagraph(MWContext* context, lo_DocState * state, LO_Position* where, LO_Position* paragraphEnd)
  5106. {
  5107.     /*
  5108.      * Search forward to find the end of the paragraph. A paragraph ends
  5109.      * at the end of the document, or before a forced break.
  5110.      */
  5111.     LO_Element* element;
  5112.     if ( ! (where && where->element ) )
  5113.     {
  5114.         XP_ASSERT(FALSE);
  5115.         return;
  5116.     }
  5117.  
  5118.     element = where->element;
  5119.     for(;;) {
  5120.         LO_Element* p = element->lo_any.next;
  5121.         if ( ! p ) break;
  5122.         element = p;
  5123.         if ( lo_IsEndOfParagraph2(context, element, 0) ) break;
  5124.     }
  5125.     paragraphEnd->element = element;
  5126.     paragraphEnd->position = lo_GetElementLength(element);
  5127.     if ( paragraphEnd->element->type == LO_LINEFEED )
  5128.     {
  5129.         paragraphEnd->position = 1;
  5130.     }
  5131.     /* lo_EnsureEditableSearchPrev2(context, state, ¶graphEnd->element, & paragraphEnd->position); */
  5132. }
  5133.  
  5134. PRIVATE
  5135. void
  5136. lo_FindParagraph(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* paragraph)
  5137. {
  5138.     lo_FindStartOfParagraph(context, state, where, & paragraph->begin);
  5139.     lo_FindEndOfParagraph(context, state, where, ¶graph->end);
  5140.     lo_ConvertInsertPointToSelectionEnd(context, state, ¶graph->end.element, ¶graph->end.position);
  5141.     LO_ASSERT_SELECTION(context, paragraph);
  5142. }
  5143.  
  5144. PRIVATE
  5145. void
  5146. lo_SelectParagraph(MWContext* context, lo_DocState *state, LO_Position* where)
  5147. {
  5148.     LO_Selection selection;
  5149.     lo_FindParagraph(context, state, where, &selection);
  5150.     lo_SetSelection(context, &selection, FALSE);
  5151. }
  5152.  
  5153. PRIVATE
  5154. intn pa_strcmp( PA_Block p1, PA_Block p2 )
  5155. {
  5156.     char *s1, *s2;
  5157.     intn ret;
  5158.  
  5159.     PA_LOCK( s1, char*, p1 );
  5160.     PA_LOCK( s2, char*, p2 );
  5161.     ret = XP_STRCMP( s1, s2 );
  5162.     PA_UNLOCK( p1 );
  5163.     PA_UNLOCK( p2 );
  5164.     return ret;
  5165. }
  5166.  
  5167. PRIVATE
  5168. Bool lo_AnchorsEqual( LO_AnchorData *p1, LO_AnchorData *p2 )
  5169. {
  5170.     if( pa_strcmp( p1->anchor, p2->anchor ) != 0 )
  5171.     {
  5172.         return FALSE;
  5173.     }
  5174.  
  5175.     if( p1->target == 0 && p2->target == 0 )
  5176.     {
  5177.         return TRUE;
  5178.     }
  5179.  
  5180.     if( p1->target && p2->target && pa_strcmp( p1->target, p2->target ) == 0 )
  5181.     {
  5182.         return TRUE;
  5183.     }
  5184.         
  5185.     return FALSE;
  5186. }
  5187.  
  5188. PRIVATE
  5189. Bool
  5190. lo_FindAnchorEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* result, Bool forward)
  5191. {
  5192.     LO_Element* element = where->element;
  5193.     LO_AnchorData* goal = lo_GetAnchorData(element);
  5194.     if ( ! goal ) return FALSE;
  5195.     while(1) {
  5196.         LO_Element* next = lo_GetNeighbor(element, forward);
  5197.         LO_AnchorData* next_anchor;
  5198.         if ( next == 0
  5199.                 || (next_anchor = lo_GetAnchorData(next)) == 0
  5200.                 || !lo_AnchorsEqual( next_anchor,goal )) 
  5201.         {
  5202.             break;
  5203.         }
  5204.         element = next;
  5205.     }
  5206.     /* LINEFEED elements can have anchor information. Backup.
  5207.      */
  5208.     while ( element && element->type == LO_LINEFEED ) {
  5209.         element = lo_GetNeighbor(element, !forward);
  5210.     }
  5211.     result->element = element;
  5212.     result->position = lo_GetElementEdge(element, forward);
  5213.     return TRUE;
  5214. }
  5215.  
  5216. PRIVATE
  5217. Bool
  5218. lo_FindAnchor(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* anchor)
  5219. {
  5220.     LO_Selection selection;
  5221.     Bool result = lo_FindAnchorEdge(context, state, where, & selection.begin, FALSE) &&
  5222.         lo_FindAnchorEdge(context, state, where, &selection.end, TRUE);
  5223.     if ( result ) {
  5224.         lo_ConvertInsertPointToSelectionEnd(context, state, &selection.end.element, &selection.end.position);
  5225.         LO_ASSERT_SELECTION(context, &selection);
  5226.         *anchor = selection;
  5227.     }
  5228.     return result;
  5229. }
  5230.  
  5231. Bool
  5232. lo_SelectAnchor(MWContext* context, lo_DocState *state, LO_Element* eptr)
  5233. {
  5234.     /* Select the entire anchor associated with eptr */
  5235.     Bool result = FALSE;
  5236.     LO_Selection selection;
  5237.     LO_Position where;
  5238.     where.element = eptr;
  5239.     where.position = 0;
  5240.     result = lo_FindAnchor(context, state, &where, &selection);
  5241.     if ( result ) {
  5242.         lo_SetSelection(context, &selection, FALSE);
  5243.     }
  5244.     return result;
  5245. }
  5246.  
  5247. /*
  5248.  * This should move to a public header so that the routine can be internationalized more easily.
  5249.  *
  5250.  * The word-finding algorithm assumes that characters can be divided into different classes.
  5251.  * The classes are: single, space, and grouping. There are currently only two grouping
  5252.  * classes, because that's enough for english text. Some languages (e.g. Japanese, which has
  5253.  * romanji, katakana and hirigana, might encourage us to define more grouping classes.)
  5254.  
  5255.  */
  5256.  
  5257. #define LO_CC_SINGLE 0
  5258. #define LO_CC_SPACE 1
  5259. #define LO_CC_ALPHA 2
  5260. #define LO_CC_PUNCT 3
  5261. #define LO_CC_KANJI 4
  5262. #define LO_CC_KANA  5
  5263. #define LO_CC_OTHERS 6
  5264.  
  5265.  
  5266. PRIVATE
  5267. intn
  5268. lo_CharacterClassOf(MWContext* context, lo_DocState *state, LO_Position* where)
  5269. {
  5270.     LO_Position position = *where;
  5271.     if ( ! lo_NormalizeSelectionPoint(context, state, &position.element, &position.position) )
  5272.     {
  5273.         /* We've gone off the end of the world. */
  5274.         return LO_CC_SINGLE;
  5275.     }
  5276.     switch ( position.element->type )
  5277.     {
  5278.     case LO_LINEFEED:
  5279.         return LO_CC_SPACE;
  5280.     case LO_TEXT:
  5281.         {
  5282.             int16 charset;
  5283.             if ( position.element->lo_text.text_len <= 0 ) {
  5284.                 return LO_CC_SPACE;
  5285.             }
  5286.             charset = position.element->lo_text.text_attr->charset;
  5287.             XP_ASSERT(position.position < position.element->lo_text.text_len);
  5288.             if (INTL_CharSetType(charset) == SINGLEBYTE)
  5289.             {
  5290.                 /* Need to handle this on a character-set basis. */
  5291.                  char *tptr;
  5292.                 char c;
  5293.  
  5294.                 /* To do: The lock should be outside of the inner loop. */
  5295.                 PA_LOCK(tptr, char *, position.element->lo_text.text);
  5296.                 if ( tptr )
  5297.                 {
  5298.                     c = tptr[position.position];
  5299.                 }
  5300.                 else
  5301.                 {
  5302.                     XP_ASSERT(FALSE);
  5303.                     c = '\0';
  5304.                 }
  5305.                 PA_UNLOCK(where->element->lo_text.text);
  5306.                 if ( XP_IS_SPACE(c) ) return LO_CC_SPACE;
  5307.                 else if ( isalnum(c) || ((unsigned char)c > 0x7F)) return LO_CC_ALPHA;
  5308.                 else return LO_CC_PUNCT;
  5309.             }
  5310.             else { 
  5311.                 /* Do something smarter here. */
  5312.                  char *tptr;
  5313.                 char c;
  5314.                 intn iRet;
  5315.  
  5316.                 /* To do: The lock should be outside of the inner loop. */
  5317.                 PA_LOCK(tptr, char *, position.element->lo_text.text);
  5318.                 if ( tptr == NULL )
  5319.                 {
  5320.                     XP_ASSERT(FALSE);
  5321.                     c = '\0';
  5322.                     iRet = LO_CC_SINGLE;
  5323.                 }
  5324.                 else
  5325.                 {
  5326.                     c = tptr[position.position];
  5327.                     /* Don't know how much detail we want to do for multibyte, 
  5328.                        right now it divide into pronounce character and kanji character
  5329.                     */
  5330.                     switch (INTL_CharClass(charset, (unsigned char *)(tptr+position.position)))
  5331.                     {
  5332.                         case SEVEN_BIT_CHAR:
  5333.                             if ( XP_IS_SPACE(c) ) iRet = LO_CC_SPACE;
  5334.                             else if ( isalnum(c) ) iRet = LO_CC_ALPHA;
  5335.                             else iRet = LO_CC_PUNCT;
  5336.                             break;
  5337.                         case HALFWIDTH_PRONOUNCE_CHAR:
  5338.                         case FULLWIDTH_PRONOUNCE_CHAR:
  5339.                             iRet = LO_CC_KANA;
  5340.                             break;
  5341.                         case FULLWIDTH_ASCII_CHAR:
  5342.                             iRet = LO_CC_ALPHA;
  5343.                             break;
  5344.                         case KANJI_CHAR:
  5345.                             iRet = LO_CC_KANJI;
  5346.                             break;
  5347.                         case UNCLASSIFIED_CHAR:
  5348.                             iRet = LO_CC_OTHERS;
  5349.                             break;
  5350.                         default:
  5351.                             iRet = LO_CC_PUNCT;
  5352.                     } 
  5353.                 }
  5354.  
  5355.                 PA_UNLOCK(where->element->lo_text.text);
  5356.  
  5357.                 return iRet;
  5358.  
  5359.             }
  5360.         }
  5361.         
  5362.     default:
  5363.         return LO_CC_SINGLE;
  5364.     }
  5365. }
  5366.  
  5367. Bool
  5368. lo_IsEdgeOfDocument2(MWContext* context, lo_DocState *state, LO_Element* element, int32 position, Bool forward)
  5369. {
  5370.     /* If we can bump the pointer, then we're not at the end of the document. */
  5371.     if ( forward )
  5372.     {
  5373.         LO_Element* next;
  5374.         if ( element->type != LO_LINEFEED
  5375.             && position < lo_GetElementLength(element) )
  5376.             return FALSE;
  5377.         next = lo_BoundaryJumpingNext(context, state, element);
  5378.         if ( next == NULL ||
  5379.             lo_BoundaryJumpingNext(context, state, next) == NULL )
  5380.             return TRUE;
  5381.     }
  5382.     else
  5383.     {
  5384.         if ( position > 0 )
  5385.             return FALSE;
  5386.     }
  5387.  
  5388.     {
  5389.         return ! lo_BumpEditablePosition(context, state, &element, &position, forward);
  5390.     }
  5391. }
  5392.  
  5393. Bool
  5394. lo_IsEdgeOfDocument(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward)
  5395. {
  5396.     return lo_IsEdgeOfDocument2(context, state, where->element, where->position, forward);
  5397. }
  5398.  
  5399. PRIVATE
  5400. Bool
  5401. lo_TraverseElement(MWContext* context, lo_DocState *state, LO_Element** element, Bool forward)
  5402. {
  5403.     if ( ! element || ! *element ) return FALSE;
  5404.     if ( forward )
  5405.     {
  5406.         *element = lo_BoundaryJumpingNext(context, state, *element);
  5407.     }
  5408.     else
  5409.     {
  5410.         *element = lo_BoundaryJumpingPrev(context, state, *element);
  5411.     }
  5412.     return *element != NULL;
  5413. }
  5414.  
  5415. /* Stop for linefeeds */
  5416. PRIVATE
  5417. Bool
  5418. lo_IsLineEdge(MWContext* context, lo_DocState *state, LO_Element* element, int32 position,
  5419.     Bool skipSoftBreaks, Bool forward)
  5420. {
  5421.     LO_Element* next = element;
  5422.     int32 nextPosition  = position;
  5423.     if ( ! lo_BumpEditablePosition(context, state, &next, &nextPosition, forward) )
  5424.     {
  5425.         /* Hit end of document. That counts as a linefeed. */
  5426.         return TRUE;
  5427.     }
  5428.  
  5429.     /* Increment element towards next, looking for a linefeed */
  5430.  
  5431.     if ( next == element )
  5432.     {
  5433.         return element->type == LO_LINEFEED;
  5434.     }
  5435.  
  5436.     if ( next->type == LO_LINEFEED )
  5437.     {
  5438.         return TRUE;
  5439.     }
  5440.  
  5441.     if ( ! lo_TraverseElement(context, state, &element, forward) ) return TRUE;
  5442.     while ( next != element )
  5443.     {
  5444.         if ( element->type == LO_LINEFEED &&
  5445.             (forward ? position <= 0 : position > 0)
  5446.             && ! lo_ValidEditableElement(context, element) )
  5447.         {
  5448.             Bool hardBreak = element->lo_linefeed.break_type != LO_LINEFEED_BREAK_SOFT;
  5449.             if ( !skipSoftBreaks || hardBreak )
  5450.                 return TRUE;
  5451.         }
  5452.  
  5453.         /* Go on to next element */
  5454.         if ( ! lo_TraverseElement(context, state, &element, forward) ) return TRUE;
  5455.     }
  5456.  
  5457.     /* Didn't find a linefeed yet. */
  5458.     return FALSE;
  5459. }
  5460.  
  5461.  
  5462. /* As long as there's room and where is of the target class, move forward.
  5463.  * returns with where not equal to the character class, unless edge of line or document.
  5464.  * returns TRUE if not edge of document, else false.
  5465.  */
  5466.  
  5467. #define LO_SO_DOCUMENT_EDGE 0
  5468. #define LO_SO_LINEFEED 1
  5469. #define LO_SO_NEW_CHARACTER_CLASS 2
  5470.  
  5471. PRIVATE
  5472. intn
  5473. lo_SkipOver(MWContext* context, lo_DocState *state, LO_Position* where, intn targetCharacterClass, Bool forward)
  5474. {
  5475.     while ( lo_CharacterClassOf(context, state, where) == targetCharacterClass )
  5476.     {
  5477.         /* Stop for linefeeds */
  5478.         if ( lo_IsLineEdge(context, state, where->element, where->position,
  5479.             targetCharacterClass == LO_CC_SPACE, forward) )
  5480.         {
  5481.             /* Skip to the next character */
  5482.             return LO_SO_LINEFEED;
  5483.         }
  5484.         if ( ! lo_BumpEditablePosition(context, state, &where->element, &where->position, forward) ) return LO_SO_DOCUMENT_EDGE;
  5485.     }
  5486.     return LO_SO_NEW_CHARACTER_CLASS;
  5487. }
  5488.  
  5489. PRIVATE
  5490. Bool
  5491. lo_MoveToNearestEdgeOfNextLine(MWContext* context, lo_DocState *state, LO_Position* where, Bool forward)
  5492. {
  5493.     LO_Position caret = *where;
  5494.     Bool bHardBreak;
  5495.     while ( ! lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, forward) )
  5496.     {
  5497.         if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
  5498.             return FALSE;
  5499.     }
  5500.     bHardBreak = caret.element && caret.element->type == LO_LINEFEED
  5501.         && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_HARD;
  5502.  
  5503.     if ( bHardBreak && ! forward && caret.position == 1 )
  5504.     {
  5505.         /* This is the after-a-hard-break-and-before-a-paragraph-end case. */
  5506.         caret.position = 0;
  5507.     }
  5508.     else if ( bHardBreak && forward && caret.position == 0 && caret.element->lo_any.next
  5509.         && caret.element->lo_any.next->type == LO_LINEFEED
  5510.         && caret.element->lo_any.next->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH )
  5511.     {
  5512.         /* This is the before-a-hard-break-before-a-paragraph-end case. */
  5513.         caret.position = 1;
  5514.     }
  5515.     else
  5516.     {
  5517.         do {
  5518.             lo_TraverseElement(context, state, &caret.element, forward);
  5519. #ifdef MQUOTE          
  5520.           } while ( caret.element && 
  5521.             ((caret.element->type == LO_LINEFEED && 
  5522.               caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT) ||
  5523.              caret.element->type == LO_BULLET));
  5524.         /* Added code above to skip over leading bullets resulting from <mquote> */
  5525. #else       
  5526.        } while ( caret.element && caret.element->type == LO_LINEFEED
  5527.              && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_SOFT);
  5528. #endif
  5529.  
  5530.  
  5531.         if ( ! caret.element )
  5532.             return FALSE;
  5533.         caret.position = 0;
  5534.         if ( ! forward && caret.element ){
  5535.             if ( ! (caret.element->type == LO_LINEFEED
  5536.                 && caret.element->lo_linefeed.break_type == LO_LINEFEED_BREAK_HARD ) ){
  5537.                 caret.position = lo_GetMaximumInsertPointPosition(caret.element);
  5538.             }
  5539.         }
  5540.         {
  5541.             /* Bump by one position if crossing from one line of wrapped text to another.
  5542.              * This is because the editor can't represent the difference between one edge
  5543.              * and the next for wrapped text. *sigh*
  5544.              */
  5545.             if ( caret.element && where->element
  5546.                 && caret.element->type == LO_TEXT && where->element->type == LO_TEXT
  5547.                 && caret.element->lo_any.edit_element == where->element->lo_any.edit_element )
  5548.             {
  5549.                 if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
  5550.                     return FALSE;
  5551.             }
  5552.         }
  5553.     }
  5554.     /* Skip over junk at edge of line. (For example, bullets.) */
  5555.     if ( ! lo_EnsureEditableSearch2(context, state, &caret.element, &caret.position, forward) )
  5556.     {
  5557.         /* Either the end of the document, or the end of a cell. */
  5558. #if 0
  5559.         if (! lo_BumpEditablePosition ( context, state, &caret.element, &caret.position, forward ) )
  5560.             return FALSE;
  5561. #endif
  5562.         return FALSE;
  5563.     }
  5564.     XP_ASSERT(caret.element != NULL);
  5565.     *where = caret;
  5566.     return TRUE;
  5567. }
  5568.  
  5569. /* Return TRUE if we actually found an edge. FALSE if we're off the start or end of the document. */
  5570.  
  5571. PRIVATE
  5572. Bool
  5573. lo_FindEdgeOfWord(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* wordEdge, 
  5574.                 Bool bSelect, Bool forward, Bool bIncludeSpacesAtEndOfWord)
  5575. {
  5576.     /*
  5577.      * Search to find the edge of the word.
  5578.      * word = { single_word | alpha_word | punct_word | space_word }
  5579.      * single_word = LO_CC_SINGLE (e.g. chinese character for the season "autumn".)
  5580.      * alpha_word = LO_CC_ALPHA + LO_CC_SPACE * (e.g. "foo10   ")
  5581.      * punct_word = LO_CC_PUNCT + LO_CC_SPACE * (e.g. "---()   ")
  5582.      * space_word = LO_CC_SPACE + (e.g. "   ")
  5583.      */
  5584.  
  5585.     intn characterClass;
  5586.     *wordEdge = *where;
  5587.  
  5588.     /* Check for end of document */
  5589.  
  5590.     if ( lo_IsEdgeOfDocument(context, state, wordEdge, forward) )
  5591.     {
  5592.         lo_FindDocumentEdge(context, state, wordEdge, bSelect, forward);
  5593.         return TRUE;
  5594.     }
  5595.  
  5596.     if ( lo_IsLineEdge(context, state, where->element, where->position, FALSE, forward) )
  5597.     {
  5598.         return TRUE;
  5599.     }
  5600.  
  5601.     if ( !forward && lo_IsLineEdge(context, state, where->element, where->position, FALSE, !forward) )
  5602.     {
  5603.         /* Starting at end of line. Move backward. */
  5604.         lo_BumpEditablePosition(context, state, &wordEdge->element, &wordEdge->position, forward);
  5605.     }
  5606.  
  5607.     characterClass = lo_CharacterClassOf(context, state, wordEdge);
  5608.     if ( characterClass == LO_CC_SINGLE ) {
  5609.         if ( forward )
  5610.         {
  5611.             lo_BumpEditablePositionForward(context, state, &wordEdge->element, &wordEdge->position);
  5612.         }
  5613.     }
  5614.     else {
  5615.         /* Match more of this class */
  5616.         if ( ! forward )
  5617.         {
  5618.             /* Skip over spaces */
  5619.             if ( lo_SkipOver(context, state, wordEdge, LO_CC_SPACE, forward ) != LO_SO_NEW_CHARACTER_CLASS )
  5620.             {
  5621.                 goto exit;
  5622.             }
  5623.             characterClass = lo_CharacterClassOf(context, state, wordEdge);
  5624.         }
  5625.         if ( characterClass != LO_CC_SPACE )
  5626.         {
  5627.             if ( lo_SkipOver(context, state, wordEdge, characterClass, forward) != LO_SO_NEW_CHARACTER_CLASS )
  5628.                 goto exit;
  5629.         }
  5630.         if ( forward )
  5631.         {
  5632.             if ( bIncludeSpacesAtEndOfWord )
  5633.             {
  5634.                 /* Skip over spaces */
  5635.                 if ( lo_SkipOver(context, state, wordEdge, LO_CC_SPACE, forward ) != LO_SO_NEW_CHARACTER_CLASS )
  5636.                     goto exit;
  5637.             }
  5638.         }
  5639.         else
  5640.         {
  5641.             /* 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. */
  5642.             lo_BumpEditablePositionForward(context, state, &wordEdge->element, &wordEdge->position);
  5643.         }
  5644.     }
  5645.  
  5646. exit:
  5647.     /* The edge might be a line feed. If so, move to an editable position. */
  5648.     lo_EnsureEditableSearch(context, state, wordEdge, forward);
  5649.     return TRUE;
  5650. }
  5651.  
  5652. PRIVATE
  5653. Bool
  5654. lo_IsEmptyText(LO_Element* eptr)
  5655. {
  5656.     if ( eptr && eptr->type == LO_TEXT &&
  5657.         lo_GetElementLength(eptr) == 0 )
  5658.     {
  5659.         return TRUE;
  5660.     }
  5661.     return FALSE;
  5662. }
  5663.  
  5664. PRIVATE
  5665. Bool
  5666. lo_FindLineEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* lineEdge, Bool select, Bool forward)
  5667. {
  5668.     /* Special case for hard breaks at end of paragraph */
  5669.  
  5670.     Bool bHardBreakAtEndOfParagraph;
  5671.  
  5672.     if ( lo_IsEdgeOfDocument(context, state, lineEdge, forward) )
  5673.     {
  5674.         lo_FindDocumentEdge(context, state, lineEdge, select, forward);
  5675.         return TRUE;
  5676.     }
  5677.  
  5678.     bHardBreakAtEndOfParagraph = lo_IsHardBreak2(context, where->element, 0)
  5679.         || (lo_IsEmptyText(where->element) && lo_IsHardBreak2(context, where->element->lo_any.next, 0));
  5680.  
  5681.     if ( bHardBreakAtEndOfParagraph && forward) {
  5682.         /* "where" is the correct edge */
  5683.         *lineEdge = *where;
  5684.     }
  5685.     else {
  5686.         LO_HitResult result;
  5687.         lo_HitLine2(context, state, where->element, where->position, where->element->lo_any.x, &result);
  5688.         if ( result.type != LO_HIT_LINE )
  5689.         {
  5690.             XP_ASSERT(FALSE);
  5691.             return FALSE;
  5692.         }
  5693.  
  5694.         if ( forward )
  5695.         {
  5696.             if ( select )
  5697.             {
  5698.                 lo_ExtendToIncludeHardBreak(context, state, & result.lo_hitLine.selection);
  5699.             }
  5700.             *lineEdge = result.lo_hitLine.selection.end;
  5701.             lo_ConvertSelectionEndToInsertPoint(context, state, &lineEdge->element, &lineEdge->position);
  5702.         }
  5703.         else
  5704.         {
  5705.             *lineEdge = result.lo_hitLine.selection.begin;
  5706.         }
  5707.     }
  5708.     return TRUE;
  5709. }
  5710.  
  5711. Bool
  5712. lo_FindDocumentEdge(MWContext* context, lo_DocState *state, LO_Position* edge, Bool select, Bool forward)
  5713. {
  5714.     LO_Element **array;
  5715.     LO_Element* eptr = NULL;
  5716. #ifdef XP_WIN16
  5717.     XP_Block *larray_array;
  5718. #endif /* XP_WIN16 */
  5719.     /*
  5720.      * Nothing to select.
  5721.      */
  5722.     if (state->line_num <= 1)
  5723.     {
  5724.         return FALSE;
  5725.     }
  5726.  
  5727.     if ( forward )
  5728.     {
  5729.         /*
  5730.          * Here we deal with the case where the current selection is within
  5731.          * a layer. In that case, we need to find the edge of the layer.
  5732.          */
  5733.         if (state->selection_layer) {
  5734.             LO_CellStruct *layer_cell = lo_GetCellFromLayer(context,
  5735.                                                        state->selection_layer);
  5736.             
  5737.             if (layer_cell)
  5738.                 eptr = layer_cell->cell_list_end;
  5739.         }
  5740.  
  5741.         if (!eptr)
  5742.         /*
  5743.          * Get last element in doc.
  5744.          */
  5745.             eptr = state->end_last_line;
  5746.         if (eptr == NULL)
  5747.         {
  5748.             eptr = state->selection_start;
  5749.         }
  5750.  
  5751.         /*
  5752.          * Since the last element is always a
  5753.          * linefeed, it is safe to set selection_end_pos to 0.
  5754.          */
  5755.         edge->element = eptr;
  5756.         edge->position = 0;
  5757.         if ( EDT_IS_EDITOR(context) ) {
  5758.             /* Skip over end-of-document elements. */
  5759.             int32 position = 0;
  5760.             while ( eptr && eptr->type == LO_LINEFEED
  5761.                 && eptr->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH ) {
  5762.                 if ( ! lo_BumpEditablePositionBackward(context, state, &eptr, &position) ) break;
  5763.             }
  5764.             edge->element = eptr;
  5765.             edge->position = position;
  5766.             /* Skip forward to end of paragraph. */
  5767.             while ( eptr && ! lo_IsEndOfParagraph2(context, eptr,0) ) {
  5768.                 eptr = eptr->lo_any.next;
  5769.             }
  5770.             if ( eptr ) {
  5771.                 edge->element = eptr;
  5772.                 edge->position = select ? 1 : 0;
  5773.             }
  5774.             else {
  5775.                 XP_ASSERT(FALSE);
  5776.             }
  5777.         }
  5778.  
  5779.     }
  5780.     else
  5781.     {
  5782.         /*
  5783.          * Here we deal with the case where the current selection is within
  5784.          * a layer. In that case, we need to find the edge of the layer.
  5785.          */
  5786.         if (state->selection_layer) {
  5787.             LO_CellStruct *layer_cell = lo_GetCellFromLayer(context,
  5788.                                                        state->selection_layer);
  5789.             
  5790.             if (layer_cell)
  5791.                 eptr = layer_cell->cell_list;
  5792.         }
  5793.  
  5794.         if (!eptr)
  5795.         {
  5796.             /*
  5797.              * Get first element in doc.
  5798.              */
  5799. #ifdef XP_WIN16
  5800.             XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  5801.             state->line_array = larray_array[0];
  5802.             XP_UNLOCK_BLOCK(state->larray_array);
  5803. #endif /* XP_WIN16 */
  5804.             
  5805.             XP_LOCK_BLOCK(array, LO_Element **, state->line_array);
  5806.             eptr = array[0];
  5807.             XP_UNLOCK_BLOCK(state->line_array);
  5808.         }
  5809.          
  5810.             /*
  5811.          * No elements.
  5812.          */
  5813.         if (eptr == NULL)
  5814.         {
  5815.             return FALSE;
  5816.         }
  5817.  
  5818.         edge->element = eptr;
  5819.         edge->position = 0;
  5820.     }
  5821.     /* Backtrack to ensure editability. */
  5822.     lo_EnsureEditableSearch(context, state, edge, !forward);
  5823.     /* However, for selecting forward, we need to go over the edge. */
  5824.     if ( select && forward ) {
  5825.         if ( edge->element->lo_any.next && edge->element->lo_any.next->type == LO_LINEFEED) {
  5826.             edge->element = edge->element->lo_any.next;
  5827.             edge->position = 1;
  5828.         }
  5829.     }
  5830.     return TRUE;
  5831. }
  5832.  
  5833. Bool
  5834. lo_FindWord(MWContext* context, lo_DocState *state, LO_Position* where, LO_Selection* word)
  5835. {
  5836.     lo_FindEdgeOfWord(context, state, where, &word->begin, TRUE, FALSE, TRUE);
  5837.     LO_ASSERT_POSITION(context, &word->begin);
  5838.     lo_FindEdgeOfWord(context, state, where, &word->end, TRUE, TRUE, FALSE);
  5839.     if ( word->begin.element == word->end.element && word->begin.position == word->end.position ) {
  5840.         /* For some reason the result is an insert point. Perhaps there are no words in
  5841.          * this document.
  5842.          */
  5843.         return FALSE;
  5844.     }
  5845.     lo_ConvertInsertPointToSelectionEnd(context, state, &word->end.element, &word->end.position);
  5846.     LO_ASSERT_SELECTION(context, word);
  5847.     return TRUE;
  5848. }
  5849.  
  5850. PRIVATE
  5851. void
  5852. lo_SelectWord(MWContext* context, lo_DocState *state, LO_Position* where)
  5853. {
  5854.     LO_Selection selection;
  5855.     Bool success = lo_FindWord(context, state, where, &selection);
  5856.     if ( success ) {
  5857.         lo_SetSelection(context, &selection, FALSE);
  5858.     }
  5859.     else {
  5860.         /* No word available. */
  5861.         lo_SelectParagraph(context, state, where );
  5862.     }
  5863. }
  5864.  
  5865. Bool lo_ProcessDoubleClick(MWContext *context, lo_TopState *top_state, lo_DocState *state, LO_HitResult* result, CL_Layer *layer)
  5866. {
  5867.     switch ( result->type )
  5868.     {
  5869.     case LO_HIT_LINE:
  5870.         {
  5871.             switch ( result->lo_hitLine.region )
  5872.             {
  5873.             case LO_HIT_LINE_REGION_BEFORE:
  5874.                 {
  5875.                     lo_SelectParagraph(context, state, & result->lo_hitLine.selection.begin );
  5876.                     return TRUE;
  5877.                }
  5878.                 break;
  5879.             case LO_HIT_LINE_REGION_AFTER:
  5880.                 {
  5881.                     LO_Selection selection = result->lo_hitLine.selection;
  5882.                     /* If they double clicked after the end of the document, back them up. */
  5883.                     lo_EnsureEditableSearchPrev2(context, state, &selection.end.element, &selection.end.position);
  5884.                     if ( lo_ExtendToIncludeHardBreak(context, state, &selection) )
  5885.                     {
  5886.                         /* Select the paragraph end */
  5887.                         selection.begin = selection.end;
  5888.                         lo_SetSelection(context, &selection, FALSE);
  5889.                     }
  5890.                     else
  5891.                     {
  5892.                         lo_SelectWord(context, state, &selection.end );
  5893.                     }
  5894.  
  5895.                     return FALSE;
  5896.                }
  5897.                 break;
  5898.             default:
  5899.                 break;
  5900.             }
  5901.         }
  5902.         break;
  5903.     case LO_HIT_ELEMENT:
  5904.         {
  5905.             switch ( result->lo_hitElement.region )
  5906.             {
  5907.             case LO_HIT_ELEMENT_REGION_BEFORE:
  5908.             case LO_HIT_ELEMENT_REGION_AFTER:
  5909.                {
  5910.                     if ( ! lo_SelectAnchor(context, state, result->lo_hitElement.position.element ) )
  5911.                         lo_SelectWord(context, state, &result->lo_hitElement.position );
  5912.                     return FALSE;
  5913.                 }
  5914.                 break;
  5915.             case LO_HIT_ELEMENT_REGION_MIDDLE:
  5916.                 {
  5917.                     /* To do: Open editor on image. For now, act like click */
  5918.                     return lo_ProcessClick( context, top_state, state, result, FALSE, layer);
  5919.                 }
  5920.                 break;
  5921.             default:
  5922.                 break;
  5923.             }
  5924.         }
  5925.         break;
  5926.     default:
  5927.         break;
  5928.     }
  5929.     return FALSE;
  5930. }
  5931.  
  5932. PRIVATE
  5933. Bool lo_FindCharacterEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* edge, Bool bSelect, Bool forward)
  5934. {
  5935.     *edge = *where;
  5936.     if ( forward )
  5937.     {
  5938.         if ( lo_IsEdgeOfDocument(context, state, edge, forward) )
  5939.         {
  5940.             lo_FindDocumentEdge(context, state, edge, bSelect, forward);
  5941.             return where->element != edge->element && where->position != edge->position;
  5942.         }
  5943.         return lo_BumpEditablePosition(context, state, &edge->element, &edge->position, forward);
  5944.     }
  5945.     else
  5946.     {
  5947.         /* We're already at the edge of the character. */
  5948.         return TRUE;
  5949.     }
  5950. }
  5951.  
  5952. PRIVATE
  5953. Bool
  5954. lo_FindChunkEdge(MWContext* context, lo_DocState *state, LO_Position* where, LO_Position* wordEdge, intn chunkType, Bool select, Bool forward)
  5955. {
  5956.     Bool result;
  5957.     *wordEdge = *where;
  5958.     switch ( chunkType )
  5959.     {
  5960.     case LO_NA_CHARACTER:
  5961.         result = lo_FindCharacterEdge(context, state, where, wordEdge, select, forward);
  5962.         break;
  5963.     case LO_NA_WORD:
  5964.         result = lo_FindEdgeOfWord(context, state, where, wordEdge, select, forward, TRUE);
  5965.         break;
  5966.     case LO_NA_LINEEDGE:
  5967.         result = lo_FindLineEdge(context, state, where, wordEdge, select, forward);
  5968.         break;
  5969.     case LO_NA_DOCUMENT:
  5970.         /* Doesn't care where we start. */
  5971.         result = lo_FindDocumentEdge(context, state, wordEdge, select, forward);
  5972.         break;
  5973.     default:
  5974.         XP_ASSERT(FALSE);
  5975.         return FALSE;
  5976.     }
  5977.     /* Did we go off the end of the world? */
  5978.     /* if ( result && ! lo_EnsureEditableSearch(context, state, wordEdge, forward) ) result = FALSE; */
  5979.     return result;
  5980. }
  5981.  
  5982. PRIVATE
  5983. Bool
  5984. lo_GapWithBothSidesAllowed( MWContext *context, lo_DocState *state, LO_Position* caret)
  5985. {
  5986.     LO_Position prev = *caret;
  5987.     LO_Position next = *caret;
  5988.     Bool result = FALSE;
  5989.     if ( caret->position == 0 )
  5990.     {
  5991.         result = lo_BumpEditablePosition(context, state, &prev.element, &prev.position, FALSE);
  5992.     }
  5993.     else if ( caret->position == lo_GetElementLength(caret->element) )
  5994.     {
  5995.         result = lo_BumpEditablePosition(context, state, &next.element, &next.position, TRUE);
  5996.     } 
  5997.     if ( ! result )
  5998.         return FALSE;
  5999.  
  6000.     /* OK, we're in the gap. */
  6001.     if ( prev.element->type == LO_HRULE 
  6002.         || next.element->type == LO_HRULE )
  6003.     {
  6004.         /* It's a gap with hrules. Now, if there isn't a hard break, we're all set. */
  6005.         LO_Element* element;
  6006.         for ( element = prev.element;
  6007.             element && element != next.element;
  6008.             element = element->lo_any.next)
  6009.         {
  6010.             if ( lo_IsHardBreak2(context, element, 0) )
  6011.             {
  6012.                 return FALSE;
  6013.             }
  6014.         }
  6015.         return TRUE;
  6016.     }
  6017.     return FALSE;
  6018. }
  6019.  
  6020. /* For logical navigation (e.g. next / prev word, character, paragraph) this function
  6021.  * performs all the book keeping to implement extension vs. moving the cursor.
  6022.  *
  6023.  * target - the unit of navigation -- the word / character that is selected.
  6024.  * bSelect - true if the current selection is to be extended.
  6025.  * bDeselecting - true if there used to be a selection, but it's going away.
  6026.  * bForward - true if the end of the target is to be used.
  6027.  *
  6028.   */
  6029.  
  6030. /* Compute new position
  6031.  */
  6032.  
  6033. Bool
  6034. LO_ComputeNewPosition( MWContext *context, intn chunkType,
  6035.     Bool bSelect, Bool bDeselecting, Bool bForward,
  6036.     LO_Element** pElement, int32* pPosition )
  6037. {
  6038.     lo_TopState* top_state;
  6039.     lo_DocState *state;
  6040.     LO_Position wordEdge;
  6041.     LO_Position caret;
  6042.     Bool result = TRUE;
  6043.  
  6044.     /* LO_DUMP_SELECTIONSTATE(context); */
  6045.  
  6046.     /*
  6047.      * Get the unique document ID, and retreive this
  6048.      * documents layout state.
  6049.      */
  6050.     top_state = lo_FetchTopState(XP_DOCID(context));
  6051.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6052.     {
  6053.         return FALSE;
  6054.     }
  6055.     state = top_state->doc_state;
  6056.  
  6057.     if ( ! pElement || ! pPosition )
  6058.         return FALSE;
  6059.  
  6060.     caret.element = *pElement;
  6061.     caret.position = *pPosition;
  6062.     wordEdge = caret;
  6063.  
  6064.     switch ( chunkType )
  6065.     {
  6066.     case LO_NA_WORD:
  6067.         {
  6068.             Bool isLineEdge = lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, bForward);
  6069.             Bool isHardBreak = lo_IsLineEdge(context, state, caret.element, caret.position, TRUE, bForward);
  6070.             if ( isLineEdge && bDeselecting ) break;
  6071.  
  6072.             if ( lo_IsEdgeOfDocument(context, state, &caret, bForward) )
  6073.             {
  6074.                 lo_FindDocumentEdge(context, state, &caret, bSelect, bForward);
  6075.                 wordEdge = caret;
  6076.             }
  6077.             else
  6078.             {
  6079.                 if ( isLineEdge )
  6080.                 {
  6081.                     Bool bOverEdge = caret.element->type == LO_LINEFEED && caret.position > 0;
  6082.                     lo_MoveToNearestEdgeOfNextLine(context, state, &caret, bForward);
  6083.                     wordEdge = caret;
  6084.                     if ( isHardBreak )
  6085.                     {
  6086.                         if ( bSelect && bOverEdge && bForward )
  6087.                         {
  6088.                             /* If the next line is a single line feed. */
  6089.                             if ( lo_GetElementLength(caret.element) == 0 )
  6090.                             {
  6091.                                 lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
  6092.                                 wordEdge = caret;
  6093.                             }
  6094.                             else
  6095.                             {
  6096.                                 lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
  6097.                                 lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6098.                             }
  6099.                         }
  6100.                     }
  6101.                     else
  6102.                     {
  6103.                         lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6104.                     }
  6105.                 }
  6106.                 else {
  6107.                     if ( isLineEdge && ! bSelect )
  6108.                     {
  6109.                         if ( isHardBreak )
  6110.                         {
  6111.                             wordEdge = caret;
  6112.                         }
  6113.                         else
  6114.                         {
  6115.                             lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6116.                         }
  6117.                     }
  6118.                     else
  6119.                     {
  6120.                         /* If we're at the beginning of a word, and we are searching backwards,
  6121.                          * we want the word before the word we're currently on.
  6122.                          */
  6123.                         if ( ! bDeselecting && !isLineEdge && !bForward )
  6124.                         {
  6125.                             lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
  6126.                         }
  6127.                         lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6128.                     }
  6129.                 }
  6130.             }
  6131.         }
  6132.         break;
  6133.     case LO_NA_CHARACTER:
  6134.         {
  6135.             Bool isLineEdge;
  6136.             /* Special rule: If navigating by unselected character, and there is a selection, then the
  6137.              * result is the edge in the direction of travel.
  6138.              */
  6139.             if ( bDeselecting )
  6140.             {
  6141.                 break;
  6142.             }
  6143.  
  6144.            isLineEdge = lo_IsLineEdge(context, state, caret.element, caret.position, FALSE, bForward);
  6145.            if ( isLineEdge )
  6146.            {
  6147.                 Bool bDoubleGap = lo_GapWithBothSidesAllowed(context, state, &caret);
  6148.                 result = lo_MoveToNearestEdgeOfNextLine(context, state, &caret, bForward);
  6149.                 if ( ! result && bSelect && bForward )
  6150.                 {
  6151.                     /* End of document case. Allowed to select the paragraph. */
  6152.                     lo_FindDocumentEdge(context, state, &caret, TRUE, TRUE);
  6153.                     result = TRUE;
  6154.                 }
  6155.                 else if ( ! result )
  6156.                 {
  6157.                     return FALSE;
  6158.                 }
  6159.                 else
  6160.                 {
  6161.                   /* If we are shift-selecting an hrule, bump an extra position,
  6162.                      because the pseudo-position before and after an hrule doesn't
  6163.                      really exist. */
  6164.                     if ( result && bSelect && bDoubleGap )
  6165.                     {
  6166.                         result = lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
  6167.                         if ( ! result )
  6168.                         {
  6169.                             /* End of document */
  6170.                             lo_FindDocumentEdge(context, state, &caret, bSelect, bForward);
  6171.                             result = TRUE;
  6172.                         }
  6173.                     }
  6174.                 }
  6175.  
  6176.                 result = TRUE;    /* Bogus -- should remove. */
  6177.                 wordEdge = caret;
  6178.            }
  6179.            else
  6180.            {
  6181.                 /* If we are searching backwards,
  6182.                  * we want the character before the character we're currently on.
  6183.                  */
  6184.                 result = TRUE;
  6185.                 if ( !bForward )
  6186.                 {
  6187.                     result = lo_BumpEditablePosition(context, state, &caret.element, &caret.position, bForward);
  6188.                 }
  6189.                 if ( result )
  6190.                 {
  6191.                     result = lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6192.                 }
  6193.             }
  6194.         }
  6195.         break;
  6196.  
  6197.     default:
  6198.         {
  6199.             result = lo_FindChunkEdge(context, state, &caret, &wordEdge, chunkType, bSelect, bForward);
  6200.         }
  6201.         break;
  6202.     }
  6203.  
  6204.     result = result && (bDeselecting || wordEdge.element != *pElement || wordEdge.position != *pPosition);
  6205.     if ( result ){
  6206.         *pElement = wordEdge.element;
  6207.         *pPosition = wordEdge.position;
  6208.     }
  6209.     return result;
  6210. }
  6211.  
  6212. /*
  6213.  * Find the next or previous word and possibly extend the selection.
  6214.  */
  6215. Bool
  6216. LO_NavigateChunk( MWContext *context, intn chunkType, Bool bSelect, Bool bForward )
  6217. {
  6218.     lo_TopState* top_state;
  6219.     lo_DocState *state;
  6220.     LO_Position caret;
  6221.     LO_Element *element;
  6222.     int32 position;
  6223.     Bool bSelectionExists;
  6224.     Bool bDeselecting;
  6225.     Bool bResult;
  6226.  
  6227.     /* LO_DUMP_SELECTIONSTATE(context); */
  6228.  
  6229.     /*
  6230.      * Get the unique document ID, and retreive this
  6231.      * documents layout state.
  6232.      */
  6233.     top_state = lo_FetchTopState(XP_DOCID(context));
  6234.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6235.     {
  6236.         return FALSE;
  6237.     }
  6238.     state = top_state->doc_state;
  6239.  
  6240.     bSelectionExists = state->selection_new == NULL;
  6241.     bDeselecting = bSelectionExists && ! bSelect;
  6242.     if ( bSelectionExists )
  6243.     {
  6244.         /* There is no insert point. Use the extension point. */
  6245.         if ( bSelect )
  6246.         {
  6247.             lo_GetExtensionPoint(context, state, &element, &position);
  6248.         }
  6249.         else
  6250.         {
  6251.             lo_GetSelectionEdge(context, state, &element, &position, bForward);
  6252.         }
  6253.     }
  6254.     else
  6255.     {
  6256.         element = state->selection_new;
  6257.         position = state->selection_new_pos;
  6258.     }
  6259.  
  6260.     bResult = LO_ComputeNewPosition(context, chunkType, bSelect, bDeselecting, bForward, &element, &position);
  6261.     if ( bResult ) {
  6262.         caret.element = element;
  6263.         caret.position = position;
  6264.         if ( bSelect )
  6265.         {
  6266.             lo_ExtendSelectionToPosition2(context, top_state, state, caret.element, caret.position);
  6267.         }
  6268.         else
  6269.         {
  6270.             if ( ! lo_EnsureEditableSearch(context, state, &caret, bForward) ) {
  6271.                 /* Off the edge of the world. */
  6272.                 lo_EnsureEditableSearch(context, state, &caret, !bForward);
  6273.             }
  6274.             lo_SetInsertPoint(context, top_state, caret.element, caret.position, state->selection_layer);
  6275.         }
  6276.     }
  6277.  
  6278.     return bResult;
  6279. }
  6280.  
  6281. void LO_GetEffectiveCoordinates( MWContext *pContext, LO_Element *pElement, int32 position,
  6282.      int32* pX, int32* pY, int32* pWidth, int32* pHeight )
  6283. {
  6284.     *pY = pElement->lo_any.y + pElement->lo_any.y_offset;
  6285.     *pX = pElement->lo_any.x + pElement->lo_any.x_offset;
  6286.     *pWidth = pElement->lo_any.width;
  6287.     *pHeight = pElement->lo_any.height;
  6288.     switch ( pElement->type )
  6289.     {
  6290.     case LO_IMAGE:
  6291.         *pWidth += 2 * pElement->lo_image.border_width;
  6292.         *pHeight += 2 * pElement->lo_image.border_width;
  6293.         if ( position > 0 ) {
  6294.             *pX = *pX + *pWidth;
  6295.         }
  6296.         break;
  6297.     case LO_TEXT:
  6298.         {
  6299.             int32 start = LO_TextElementWidth( pContext, (LO_TextStruct*)pElement, 
  6300.                             position);
  6301.             *pX += start;
  6302.         }
  6303.         break;
  6304.     case LO_HRULE:
  6305.         /* The effective y and height is the height of the following linefeed. */
  6306.         {
  6307.             LO_Element* pNext = pElement->lo_any.next;
  6308.             if ( pNext && pNext->type == LO_LINEFEED )
  6309.             {
  6310.                 int32 dummyWidth;
  6311.                 int32 dummyX;
  6312.                 LO_GetEffectiveCoordinates(pContext, pNext, 0, &dummyX, pY, &dummyWidth, pHeight);
  6313.             }
  6314.             if ( position > 0 ) {
  6315.                 *pX = *pX + *pWidth;
  6316.             }
  6317.         }
  6318.         break;
  6319.     case LO_LINEFEED:
  6320.         {
  6321.             if ( position > 0 )
  6322.             {
  6323.                 /* At start of next element. */
  6324.                 LO_Element* pNext = pElement->lo_any.next;
  6325.                 if ( pNext )
  6326.                 { /* Just one level of recursion, since position = 0 */
  6327.                     LO_GetEffectiveCoordinates(pContext, pNext, 0, pX, pY, pWidth, pHeight);
  6328.                 }
  6329.             }
  6330.         }
  6331.         break;
  6332.     default:
  6333.         break;
  6334.     }
  6335. }
  6336.  
  6337. PRIVATE
  6338. Bool lo_FindClosestUpDown(MWContext *pContext, lo_DocState* state, int32 x, int32 y,
  6339.                           Bool bForward, int32* ret_x, int32* ret_y)
  6340. {
  6341.     int32 iLine;
  6342.     Bool bFound = FALSE;
  6343.     lo_TopState* top_state = lo_FetchTopState(XP_DOCID(pContext));
  6344.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6345.     {
  6346.         return FALSE;
  6347.     }
  6348.  
  6349.     /* find the line we are currently on */
  6350.     iLine = lo_PointToLine( pContext, state, x, y );
  6351.  
  6352.     if ( bForward )
  6353.     {
  6354.         int32 maxLine;
  6355.         /* Down. MaxLine is - 2 for the lines data structure */
  6356.         maxLine = state->line_num - 2;
  6357.         if ( EDT_IS_EDITOR(pContext) && top_state->doc_state == state )
  6358.         {
  6359.             maxLine -= 2; /* and -2 for the phantom end-of-doc hrule*/
  6360.         }
  6361.         for( ;
  6362.             iLine <= maxLine  && !bFound;
  6363.             iLine++)
  6364.         {
  6365.             bFound = lo_FindBestPositionOnLine( pContext, state, iLine, x, y, bForward, ret_x, ret_y );
  6366.         }
  6367.     }
  6368.     else
  6369.     {
  6370.         /* Up */ /*changed iline comparisson to include 0.  is this correct?  uparrow was not working*/
  6371.         for( ;
  6372.             iLine >= 0 && !bFound;
  6373.             --iLine)
  6374.         {
  6375.             bFound = lo_FindBestPositionOnLine( pContext, state, iLine, x, y, bForward, ret_x, ret_y );
  6376.         }
  6377.     }
  6378.     return bFound;
  6379. }
  6380.  
  6381.  
  6382. /*
  6383.  * Find a element up from this and set the cursor there.
  6384.  */
  6385. void LO_UpDown( MWContext *pContext, LO_Element *pElement, int32 position, int32 iDesiredX, Bool bSelect, Bool bForward )
  6386. {
  6387. #ifdef EDITOR
  6388.     lo_TopState* top_state;
  6389.     lo_DocState *state;
  6390.     int32 x, y, width, height;
  6391.     Bool bFound = FALSE;
  6392.     LO_Element *pNext;/*used to look ahead for correct y*/
  6393.     /*
  6394.      * Get the unique document ID, and retreive this
  6395.      * documents layout state.
  6396.      */
  6397.     top_state = lo_FetchTopState(XP_DOCID(pContext));
  6398.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6399.     {
  6400.         return;
  6401.     }
  6402.     state = top_state->doc_state;
  6403.  
  6404.     /* Special case. If the element is a linefeed and the position is 1,
  6405.      * we have to move the cursor to the next element.
  6406.      */
  6407.     if ( pElement && pElement->type == LO_LINEFEED && position > 0 ) {
  6408.         LO_Element* pNext = pElement->lo_linefeed.next;
  6409.         if ( pNext ) {
  6410.             pElement = pNext;
  6411.             position = 0;
  6412.         }
  6413.     }
  6414.  
  6415.     {
  6416.         LO_Position p;
  6417.         p.element = pElement;
  6418.         p.position = position;
  6419.         if ( lo_IsEdgeOfDocument(pContext, state, &p, bForward) )
  6420.         {
  6421.             LO_NavigateChunk( pContext, LO_NA_DOCUMENT, bSelect, bForward);
  6422.             return;
  6423.         }
  6424.     }
  6425.  
  6426.     /* Find the effective coordinate of our current position */
  6427.     LO_GetEffectiveCoordinates(pContext, pElement, position, &x, &y, &width, &height);
  6428.  
  6429.     /*  Skip over this element */
  6430.     pNext=NULL; /*if not forward, let the old way handle it.*/
  6431.     if (bForward)
  6432.     {
  6433.         pNext=pElement->lo_any.next;
  6434.         while (pNext && (pNext->type!=LO_CELL) && (!lo_ValidEditableElement(pContext,pNext) || (pNext->lo_any.y == pElement->lo_any.y)) )
  6435.             pNext=pNext->lo_any.next;
  6436.         /*if we find a cell, we will need more work.*/
  6437.         if (pNext)
  6438.             y=pNext->lo_any.y;
  6439.     }
  6440.     if (!pNext)
  6441.     {
  6442.         if ( bForward) {
  6443.             y = pElement->lo_any.y + pElement->lo_any.line_height;
  6444.         }
  6445.         else {
  6446.             y = pElement->lo_any.y - 1;
  6447.         }
  6448.     }
  6449.  
  6450.     bFound = lo_FindClosestUpDown(pContext, state, iDesiredX, y, bForward, &x, &y);
  6451.  
  6452.     /* We found a text element on this line.  Now, lets re-scan the line for 
  6453.      *    the best fit.
  6454.     */
  6455.     if( bFound ){
  6456.         if( bSelect ){
  6457.             EDT_ExtendSelection( pContext, x, y );
  6458.             EDT_EndSelection( pContext, x, y );
  6459.         }
  6460.         else {
  6461.             LO_PositionCaret( pContext, x, y, NULL );
  6462.         }
  6463.     }
  6464.     else {
  6465.         /* No more elements. */
  6466.         /* Navigate to the edge of the document. */
  6467.         LO_NavigateChunk( pContext, LO_NA_DOCUMENT, bSelect, bForward);
  6468.     }
  6469. #endif
  6470. }
  6471.  
  6472. /* like lo_search_element_list, but matches nearest X */
  6473. LO_Element *
  6474. lo_search_element_list_WideMatch(MWContext *context, LO_Element *eptr, LO_Element* eEndPtr, int32 x, int32 y,
  6475.                                  Bool bForward)
  6476. {
  6477.     LO_Element* pFound = NULL;
  6478.     int32 bestDistanceX = 2000000;
  6479.     int32 bestDistanceY = 2000000;
  6480.     int32 distanceY;
  6481.  
  6482.     for ( ;
  6483.         eptr != eEndPtr && eptr != NULL;
  6484.         eptr = eptr->lo_any.next
  6485.         )
  6486.     {
  6487.         int32 width, height;
  6488.  
  6489.         if( eptr->type != LO_CELL
  6490.             && eptr->type != LO_TABLE
  6491.             && ! lo_ValidEditableElementIncludingParagraphMarks(context, eptr)){
  6492.             continue;
  6493.         }
  6494.         width = eptr->lo_any.width;
  6495.         /*
  6496.          * Images need to account for border width
  6497.          */
  6498.         if (eptr->type == LO_IMAGE)
  6499.         {
  6500.             width = width + (2 * eptr->lo_image.border_width);
  6501.         }
  6502.         if (width <= 0)
  6503.         {
  6504.             width = 1;
  6505.         }
  6506.  
  6507.         height = eptr->lo_any.height;
  6508.         /*
  6509.          * Images need to account for border height
  6510.          */
  6511.         if (eptr->type == LO_IMAGE)
  6512.         {
  6513.             height = height + (2 * eptr->lo_image.border_width);
  6514.         }
  6515.  
  6516.         distanceY = bestDistanceY + 1;
  6517.         if ( y < eptr->lo_any.y ) {
  6518.             /* This element is below the target. Consider it if we're searching forward. */
  6519.             if ( bForward ) {
  6520.                 distanceY = eptr->lo_any.y - y;
  6521.             }
  6522.         }
  6523.         else if ( eptr->lo_any.y + eptr->lo_any.y_offset + height <= y ) {
  6524.             /* This element is above the target. Consider it if we're searching backwards. */
  6525.             if ( ! bForward ) {
  6526.                 distanceY = y - (eptr->lo_any.y + eptr->lo_any.y_offset + height) + 1;
  6527.             }
  6528.         }
  6529.         else {
  6530.             distanceY = 0;
  6531.         }
  6532.         if ( distanceY <= bestDistanceY ) {
  6533.                 if ( distanceY < bestDistanceY ) {
  6534.                     /* We have a new winner in Y, so forget our X */
  6535.                     bestDistanceX = 2000000;
  6536.                     bestDistanceY = distanceY;
  6537.                 }
  6538.                 
  6539.                 
  6540.             /* We're looking for the closest match */
  6541.             if ((x < (eptr->lo_any.x + eptr->lo_any.x_offset +
  6542.                 width))&&(x >= eptr->lo_any.x))
  6543.             {
  6544.                 /* Can't get closer than containing. */
  6545.                 pFound = eptr;
  6546.                 bestDistanceX = 0;
  6547.                 if ( distanceY == 0 ) {
  6548.                     break;
  6549.                 }
  6550.             }
  6551.             else {
  6552.                 int32 distance;
  6553.                 if ( x < eptr->lo_any.x ) {
  6554.                     distance = eptr->lo_any.x - x;
  6555.                 }
  6556.                 else {
  6557.                     distance = x - (eptr->lo_any.x + eptr->lo_any.x_offset +
  6558.                         width) + 1;
  6559.                 }
  6560.                 if ( distance < bestDistanceX ) {
  6561.                     pFound = eptr;
  6562.                     bestDistanceX = distance;
  6563.                 }
  6564.             }
  6565.         }
  6566.         /* Skip over cells while searchig for the closest item */
  6567.         if (eptr->type == LO_TABLE) {
  6568.             while ( eptr->lo_any.next && eptr->lo_any.next->type == LO_CELL )
  6569.             {
  6570.                 eptr = eptr->lo_any.next;
  6571.             }
  6572.         }
  6573.     }
  6574.     return pFound;
  6575. }
  6576.  
  6577. PRIVATE
  6578. Bool lo_FindClosestUpDown_SubDoc(MWContext *pContext, lo_DocState* state,
  6579.                                  LO_SubDocStruct* pSubdoc, int32 x, int32 y,
  6580.                           Bool bForward, int32* ret_x, int32* ret_y)
  6581. {
  6582.     /* Cribbed from the end of lo_XYToDocumentElement2 */
  6583.     int32 new_x, new_y;
  6584.  
  6585.     new_x = x - (pSubdoc->x + pSubdoc->x_offset +
  6586.         pSubdoc->border_width);
  6587.     new_y = y - (pSubdoc->y + pSubdoc->y_offset +
  6588.         pSubdoc->border_width);
  6589.     return lo_FindClosestUpDown(pContext, (lo_DocState *)pSubdoc->state, new_x, new_y, bForward, ret_x, ret_y);
  6590. }
  6591.  
  6592. Bool lo_FindClosestUpDown_Cell(MWContext *pContext, lo_DocState* state, 
  6593.                                LO_CellStruct* cell, int32 x, int32 y,
  6594.                           Bool bForward, int32* ret_x, int32* ret_y)
  6595. {
  6596.     /* Cells don't have line arrays. So we linearly search for our element.
  6597.      * This code was inspired by the code in lo_search_element_list and
  6598.      * lo_XYToCellElement
  6599.      */
  6600.     LO_Element *eptr;
  6601.     LO_Element *element;
  6602.  
  6603.     eptr = cell->cell_list;
  6604.     element = lo_search_element_list_WideMatch(pContext, eptr, NULL, x, y, bForward);
  6605.     if (element == NULL)
  6606.     {
  6607.         eptr = cell->cell_float_list;
  6608.         element = lo_search_element_list_WideMatch(pContext, eptr, NULL, x, y, bForward);
  6609.     }
  6610.     if ((element != NULL)&&(element->type == LO_SUBDOC))
  6611.     {
  6612.         return lo_FindClosestUpDown_SubDoc(pContext, state, (LO_SubDocStruct *)element,
  6613.             x, y, bForward, ret_x, ret_y);
  6614.     }
  6615.     else if ((element != NULL)&&(element->type == LO_CELL))
  6616.     {
  6617.         return lo_FindClosestUpDown_Cell(pContext, state,
  6618.             (LO_CellStruct *)element, x, y,
  6619.             bForward, ret_x, ret_y);
  6620.     }
  6621.     else if ((element != NULL)&&(element->type == LO_TABLE))
  6622.     {
  6623.         if ( lo_FindBestPositionInTable(pContext, state,
  6624.             (LO_TableStruct *)element, x, y,
  6625.             bForward, ret_x, ret_y) )
  6626.             return TRUE;
  6627.         else {
  6628.             /* no more stuff in the table. Skip out of the table and try again. */
  6629.             if ( bForward ) {
  6630.                 y = element->lo_table.y + element->lo_table.y_offset + element->lo_table.height;
  6631.             }
  6632.             else {
  6633.                 y = element->lo_table.y - 1;
  6634.             }
  6635.             /* Tail recursion. Maybe we should use goto? */
  6636.             return lo_FindClosestUpDown_Cell(pContext, state, cell, x, y, bForward, ret_x, ret_y);
  6637.         }
  6638.     }
  6639.     else if (element != NULL) {
  6640.         /* Return this element's Y. */
  6641.         int32 eHeight;
  6642.         int32 eWidth;
  6643.         int32 eX;
  6644.         int32 eY;
  6645.         LO_GetEffectiveCoordinates(pContext, element, 0, &eX, &eY, & eWidth, &eHeight);
  6646.         /* Clip x to be inside the bounds of the element */
  6647.         if ( x >= eX + eWidth ) x = eX + eWidth - 1;
  6648.         if ( x < eX ) x = eX;
  6649.         *ret_x = x;
  6650.         *ret_y = eY;
  6651.         return TRUE;
  6652.     }
  6653.     else
  6654.         return FALSE;
  6655. }
  6656.  
  6657. PRIVATE
  6658. LO_CellStruct* lo_FindBestCellInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX, 
  6659.             int32 iDesiredY, Bool bForward )
  6660. {
  6661.     /* Find end of table */
  6662.     LO_Element* pClosest = NULL;
  6663.     LO_CellStruct* result = NULL;
  6664.     if ( pTable ) {
  6665.         LO_Element* pStart = pTable->next;
  6666.         LO_Element* pEnd = pStart;
  6667.         while ( pEnd && pEnd->type == LO_CELL ) {
  6668.             pEnd = pEnd->lo_any.next;
  6669.         }
  6670.  
  6671.         if ( pStart ) {
  6672.             pClosest = lo_search_element_list_WideMatch(context, pStart, pEnd, iDesiredX, iDesiredY, bForward);
  6673.  
  6674.             if ( pClosest && pClosest->type == LO_CELL ) {
  6675.                 return result = &pClosest->lo_cell;
  6676.             }
  6677.         }
  6678.     }
  6679.     return result;
  6680. }
  6681.  
  6682.  
  6683. Bool lo_FindBestPositionInTable(MWContext *context, lo_DocState* state, LO_TableStruct* pTable, int32 iDesiredX, 
  6684.             int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY )
  6685. {
  6686.     /* Up/Down can skip over cell boundaries. So we may have to search in two cells, the one
  6687.      * we started in, and the next one in the direction we're searching.
  6688.      * For simplicity, we search a cell, and if we don't hit anything, we bump the
  6689.      * y coordinate by the cell height, and search again.
  6690.      */
  6691.  
  6692.     while(1) {
  6693.         LO_CellStruct* pCell = lo_FindBestCellInTable(context, state, pTable, iDesiredX, iDesiredY, bForward);
  6694.         if ( ! pCell ) return FALSE;
  6695.         if ( lo_FindClosestUpDown_Cell(context, state, pCell, iDesiredX, iDesiredY, bForward, pRetX, pRetY) ) {
  6696.             return TRUE;
  6697.         }
  6698.         /* Search the next cell */
  6699.         if ( ! bForward ) {
  6700.             iDesiredY = pCell->y - 1;
  6701.         }
  6702.         else {
  6703.             iDesiredY = pCell->y + pCell->y_offset + pCell->height;
  6704.         }
  6705.     }
  6706. }
  6707.  
  6708. Bool lo_FindBestPositionOnLine(MWContext *context, lo_DocState* state, int32 iLine, int32 iDesiredX, 
  6709.             int32 iDesiredY, Bool bForward, int32* pRetX, int32 *pRetY )
  6710. {
  6711.     LO_Element **line_array;
  6712.     LO_Element *pElement, *pEnd;
  6713.     LO_Element *pFound = NULL;
  6714.     Bool bDone = FALSE;
  6715.     
  6716.     XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
  6717.  
  6718.     if( iLine == state->line_num - 2 ){
  6719.         pEnd = 0;
  6720.     }
  6721.     else {
  6722.         pEnd = line_array[iLine+1];
  6723.     }
  6724.  
  6725.     pElement = line_array[iLine];
  6726.  
  6727.     XP_UNLOCK_BLOCK(state->line_array);
  6728.  
  6729.     if ( pElement->type == LO_TABLE ) {
  6730.         return lo_FindBestPositionInTable(context, state, & pElement->lo_table, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
  6731.     }
  6732.  
  6733.     for( ;
  6734.         pElement != pEnd && !bDone;
  6735.         pElement = pElement->lo_any.next){
  6736.  
  6737.         if( lo_ValidEditableElementIncludingParagraphMarks(context, pElement)){
  6738.             if( pFound ){
  6739.                 /* what is desired is before the element, take the absolute
  6740.                  *  value of the distance between the two elements
  6741.                  */
  6742.                 if( iDesiredX < pElement->lo_any.x ){
  6743.                     if( pElement->lo_any.x - iDesiredX < iDesiredX - (*pRetX) ){
  6744.                         *pRetX = pElement->lo_any.x;
  6745.                         *pRetY = pElement->lo_any.y;
  6746.                         pFound = pElement;
  6747.                     }
  6748.                     bDone = TRUE;
  6749.                 }
  6750.                 else if( iDesiredX < pElement->lo_any.x + pElement->lo_any.width ) {
  6751.                     *pRetY = pElement->lo_any.y;
  6752.                     *pRetX = iDesiredX;
  6753.                     pFound = pElement;
  6754.                     bDone = TRUE;
  6755.                 }
  6756.                 else {
  6757.                     *pRetX = iDesiredX;
  6758.                     /* *pRetX = pElement->lo_any.x + pElement->lo_any.width; */
  6759.                     *pRetY = pElement->lo_any.y;
  6760.                     pFound = pElement;
  6761.                 }
  6762.             }
  6763.             else {
  6764.                 if( iDesiredX < pElement->lo_any.x ){
  6765.                     *pRetX = pElement->lo_any.x;
  6766.                     *pRetY = pElement->lo_any.y;
  6767.                     bDone = TRUE;
  6768.                 }
  6769.                 else if( iDesiredX < pElement->lo_any.x + pElement->lo_any.width ) {
  6770.                     *pRetY = pElement->lo_any.y;
  6771.                     *pRetX = iDesiredX;
  6772.                     bDone = TRUE;
  6773.                 }
  6774.                 else {
  6775.                     *pRetX = iDesiredX;
  6776.                     /* *pRetX = pElement->lo_any.x + pElement->lo_any.width; */
  6777.                     *pRetY = pElement->lo_any.y;
  6778.                 }
  6779.                 pFound = pElement;
  6780.             }
  6781.         }
  6782.     }
  6783.  
  6784.     if ( pFound && pFound->type == LO_CELL ) {
  6785.         return lo_FindClosestUpDown_Cell(context, state, &pFound->lo_cell, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
  6786.     }
  6787.     else if ( pFound && pFound->type == LO_SUBDOC ) {
  6788.         return lo_FindClosestUpDown_SubDoc(context, state, &pFound->lo_subdoc, iDesiredX, iDesiredY, bForward, pRetX, pRetY);
  6789.     }
  6790.     return pFound != NULL;
  6791. }
  6792.  
  6793. ED_Buffer* LO_GetEDBuffer( MWContext *context)
  6794. {
  6795. #ifdef EDITOR
  6796.     int32 doc_id;
  6797.     lo_TopState *top_state;
  6798.  
  6799.     /*
  6800.      * Get the unique document ID, and retreive this
  6801.      * documents layout state.
  6802.      */
  6803.     doc_id = XP_DOCID(context);
  6804.     top_state = lo_FetchTopState(doc_id);
  6805.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6806.     {
  6807.         return NULL;
  6808.     }
  6809.     return top_state->edit_buffer;
  6810. #else
  6811.     return 0;
  6812. #endif
  6813. }
  6814.  
  6815. /* #endif */
  6816.  
  6817. /* 
  6818.  * Given a Text element and a character offset, find the width of upto that
  6819.  *    offset.
  6820. */
  6821. int32
  6822. LO_TextElementWidth(MWContext *context, LO_TextStruct *text_ele, int charOffset)
  6823. {
  6824.     LO_TextInfo text_info;
  6825.     int orig_len;
  6826.     int32 retval;
  6827.  
  6828.     if ((text_ele->text == NULL)||(text_ele->text_len <= 0))
  6829.     {
  6830.         return(0);
  6831.     }
  6832.  
  6833.     orig_len = text_ele->text_len;
  6834.     text_ele->text_len = charOffset;
  6835.     FE_GetTextInfo(context, text_ele, &text_info);
  6836.     retval =  text_info.max_width;
  6837.     text_ele->text_len = orig_len;
  6838.     return retval;
  6839. }
  6840.  
  6841. #ifdef TEST_16BIT
  6842. #undef XP_WIN16
  6843. #endif /* TEST_16BIT */
  6844.  
  6845. #ifdef PROFILE
  6846. #pragma profile off
  6847. #endif
  6848.  
  6849. /* #ifndef NO_TAB_NAVIGATION  */
  6850.  
  6851. /* 
  6852. Arthur Liu, 9/13/96
  6853.   Reading through this file, I found LO_getDocState() should
  6854.   be a separate function.
  6855.  
  6856.   TODO, check this file(may be also other files), to replace
  6857.   the duplicated code with this function.
  6858. */
  6859. PRIVATE
  6860. lo_DocState *
  6861. LO_getDocState(MWContext *context)
  6862. {
  6863.     int32 doc_id;
  6864.     lo_TopState *top_state;
  6865.  
  6866.     /*
  6867.      * Get the unique document ID, and retreive this
  6868.      * documents layout state.
  6869.      */
  6870.     doc_id = XP_DOCID(context);
  6871.     top_state = lo_FetchTopState(doc_id);
  6872.     if ((top_state == NULL)||(top_state->doc_state == NULL))
  6873.     {
  6874.         return NULL ;
  6875.     }
  6876.     return( top_state->doc_state );
  6877. }
  6878.  
  6879. /* 
  6880. Arthur Liu, 9/13/96
  6881.   LO_getFirstLastElement() was created for Keyboard Navigation. 
  6882.   
  6883.     TODO:  Its function is a subset of lo_FindDocumentEdge(..), and the 
  6884.     latter may call LO_getFirstLastElement() to avoid code duplication.
  6885. */
  6886.  
  6887. LO_Element * 
  6888. LO_getFirstLastElement(MWContext *context, int forward )
  6889. {
  6890.     lo_DocState *state;
  6891.     LO_Element **array;
  6892.     LO_Element* eptr;
  6893. #ifdef XP_WIN16
  6894.     XP_Block *larray_array;
  6895. #endif /* XP_WIN16 */
  6896.     
  6897.     if( NULL == ( state = LO_getDocState(context) ) )
  6898.         return( NULL );
  6899.  
  6900.     /*
  6901.      * Nothing to select.
  6902.      */
  6903.     if (state->line_num <= 1)
  6904.     {
  6905.         return NULL ;
  6906.     }
  6907.  
  6908.     if ( ! forward )
  6909.     {
  6910.         /*
  6911.          * Get last element in doc.
  6912.          */
  6913.         eptr = state->end_last_line;
  6914.         if (eptr == NULL)
  6915.         {
  6916.             eptr = state->selection_start;        /* ??? */
  6917.         }
  6918.     }
  6919.     else    /* wantFirst */
  6920.     {
  6921.         /*
  6922.          * Get first element in doc.
  6923.          */
  6924. #ifdef XP_WIN16
  6925.         XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
  6926.         state->line_array = larray_array[0];
  6927.         XP_UNLOCK_BLOCK(state->larray_array);
  6928. #endif /* XP_WIN16 */
  6929.  
  6930.         XP_LOCK_BLOCK(array, LO_Element **, state->line_array);
  6931.         eptr = array[0];
  6932.         XP_UNLOCK_BLOCK(state->line_array);
  6933.         /*
  6934.          * No elements.
  6935.          */
  6936.         if (eptr == NULL)
  6937.         {
  6938.             return NULL ;
  6939.         }
  6940.  
  6941.     }
  6942.     return( eptr );
  6943. }    /* LO_getFirstLastElement() */
  6944.  
  6945. /*
  6946.  return TRUE if pCurrentFocus->pElement is a tabable element,
  6947.  may also fill in pCurrentFocus->pAnchor, and pCurrentFocus->mapAreaIndex
  6948. */
  6949. Bool 
  6950. LO_isTabableElement(MWContext *context, LO_TabFocusData *pCurrentFocus  )
  6951. {
  6952.     /*
  6953.         For all LO_ types which update status bar, see CWinCX::OnMouseMoveForLayerCX(UINT uFlags, CPoint& cpPoint,
  6954.         for all links see CWinCX::OnLButtonUpForLayerCX(UINT uFlags, CPoint& cpPoint, XY& Point,
  6955.     */
  6956.     Bool                    bIsTabable;
  6957.     LO_TextStruct            *pText;
  6958.     LO_ImageStruct            *pImage;
  6959.     lo_MapAreaRec            *tempArea;
  6960.     LO_Element                *pElement;
  6961.     LO_FormElementStruct    *formEleStruct;
  6962.  
  6963.     if( pCurrentFocus == NULL )
  6964.         return( FALSE );
  6965.  
  6966.     pCurrentFocus->mapAreaIndex = 0;            /* no area */
  6967.     pCurrentFocus->pAnchor        = NULL;
  6968.  
  6969.     pElement = pCurrentFocus->pElement;
  6970.     if( pElement == NULL )
  6971.         return( FALSE );
  6972.  
  6973.     bIsTabable = FALSE;
  6974.  
  6975.     switch( pElement->type ) {
  6976.     
  6977.     case LO_NONE :        /*     0  */
  6978.         break;
  6979.     case LO_TEXT :        /*     1  */
  6980.         pText = (LO_TextStruct *) pElement;
  6981.         if( pText )
  6982.             bIsTabable = (pText->anchor_href && pText->anchor_href->anchor);
  6983.         if( bIsTabable )
  6984.             pCurrentFocus->pAnchor = pText->anchor_href;
  6985.         break;
  6986.  
  6987.     case LO_LINEFEED :    /*     2  */
  6988.         break;
  6989.     case LO_HRULE :        /*     3  */
  6990.         break;
  6991.     case LO_IMAGE :        /*     4  */
  6992.         pImage = ( LO_ImageStruct *) pElement;
  6993.         
  6994.         /* first test image with a link */
  6995.         bIsTabable  = (pImage->anchor_href != NULL) && (pImage->anchor_href->anchor != NULL);
  6996.         if( bIsTabable ) {
  6997.             pCurrentFocus->pAnchor = pImage->anchor_href;
  6998.             break;
  6999.         }
  7000.  
  7001.         /* test use map */
  7002.         tempArea = LO_getTabableMapArea(context, pImage, 1 ); /* 1 means get the first */
  7003.         if( tempArea != NULL ) {
  7004.             pCurrentFocus->mapAreaIndex = 1;            /* the first area */
  7005.             pCurrentFocus->pAnchor        = tempArea->anchor;
  7006.             bIsTabable = TRUE;
  7007.         }
  7008.         break;
  7009.     case LO_BULLET :    /*     5 */
  7010.         break;
  7011.     case LO_FORM_ELE :    /*     6 */
  7012.         /*TODO get anchor for buttons...*/
  7013.         formEleStruct = (LO_FormElementStruct *) pElement;
  7014.         if( formEleStruct )
  7015.             bIsTabable = LO_isTabableFormElement( formEleStruct );
  7016.         break;
  7017.  
  7018.     case LO_SUBDOC :    /*     7  */
  7019.         break;
  7020.     case LO_TABLE :        /*     8  */
  7021.         break;
  7022.     case LO_CELL :        /*     9  */
  7023.         break;
  7024.     case LO_EMBED :        /*    10  */
  7025.         break;
  7026.     case LO_EDGE :        /*    11  */
  7027.         break;
  7028. #ifdef JAVA
  7029.     case LO_JAVA :        /*    12  */
  7030.         break;
  7031. #endif
  7032.     case LO_SCRIPT :    /*    13  */
  7033.         break;
  7034.     default :            /* something wrong!!  */
  7035.             bIsTabable = FALSE;
  7036.         break;
  7037.     }
  7038.     return( bIsTabable );
  7039. }
  7040.  
  7041.  
  7042. /*
  7043.     get first(last) if current element is NULL.
  7044. */
  7045. Bool
  7046. LO_getNextTabableElement( MWContext *context, LO_TabFocusData *pCurrentFocus, int forward )
  7047. {
  7048.  
  7049.     lo_DocState        *state;
  7050.     LO_Element        *currentElement;            
  7051.     lo_MapAreaRec    *tempArea;
  7052.     LO_AnchorData    *pLastAnchorData;
  7053.  
  7054.     if( NULL == ( state = LO_getDocState(context) ) )
  7055.         return( FALSE );
  7056.  
  7057.     /*
  7058.         A text may have been fragmented into multiple lines because the text folding.
  7059.         If last tab focus is a LO_Text, remember its anchor to skip continuious text fragments.
  7060.     
  7061.         When the focus is drawn, all continuious fragments will hav efocus, See
  7062.         CWinCX::setTextTabFocusDrawFlag() in cmd\winfe\cxwin.cpp file.
  7063.     */
  7064.  
  7065.     pLastAnchorData = NULL;        /* assuming no need to skip continuious text fragments.  */
  7066.         
  7067.     currentElement = pCurrentFocus->pElement;
  7068.     /* find the first candidate,   */
  7069.     if( currentElement == NULL ) {
  7070.         /* find the first(last) element in the Doc as candidate,   */
  7071.         pCurrentFocus->pElement = LO_getFirstLastElement( context, forward );        /* forward is getting first  */
  7072.  
  7073.         /*TODO go down to cell_list if pElement has subtree  */
  7074.  
  7075.     } else {
  7076.         if( pCurrentFocus->pElement->lo_any.type == LO_TEXT )
  7077.             pLastAnchorData = pCurrentFocus->pAnchor;    /* need to skip continuious text fragments.  */
  7078.         
  7079.         /* if the last element is a image map, try go to next area in the map  */
  7080.         if( currentElement->lo_any.type == LO_IMAGE ) {
  7081.                 if( forward ) {
  7082.                 pCurrentFocus->mapAreaIndex++;
  7083.                 tempArea = LO_getTabableMapArea(context, (LO_ImageStruct *)currentElement, pCurrentFocus->mapAreaIndex );
  7084.                 if( tempArea ) {
  7085.                     pCurrentFocus->pAnchor = tempArea->anchor;
  7086.                     return( TRUE );
  7087.                 }
  7088.             } else {    /* if( forward )   */
  7089.                 pCurrentFocus->mapAreaIndex--;
  7090.                 if( pCurrentFocus->mapAreaIndex > 0 ) {    /* otherwise no need to search.  */
  7091.                     tempArea = LO_getTabableMapArea(context, (LO_ImageStruct *)currentElement, pCurrentFocus->mapAreaIndex );
  7092.                     if( tempArea ) {
  7093.                         pCurrentFocus->pAnchor = tempArea->anchor;
  7094.                         return( TRUE );
  7095.                     }
  7096.                 }
  7097.             }
  7098.             
  7099.         }
  7100.         
  7101.         /* advance the pointer, use the next(previous) as first candidate.
  7102.             pElement = lo_GetNeighbor( currentElement, forward);
  7103.             pElement = currentElement;
  7104.         */
  7105.         lo_TraverseElement(context, state, &(pCurrentFocus->pElement), forward);
  7106.     }
  7107.  
  7108.     /*todo if not forward, get last area.  */
  7109.     pCurrentFocus->mapAreaIndex = 0;            /* assumming no focus area  */
  7110.     pCurrentFocus->pAnchor        = NULL;
  7111.     while ( pCurrentFocus->pElement != NULL ) {
  7112.         if( LO_isTabableElement(context, pCurrentFocus) )  {
  7113.  
  7114.             if( pLastAnchorData == NULL        /* no need to skip continuious text fragments.  */
  7115.                 || pCurrentFocus->pElement->lo_any.type != LO_TEXT 
  7116.                 || pLastAnchorData != pCurrentFocus->pAnchor )
  7117.                 return( TRUE );
  7118.             /* else it is a text fragment with the same anchor as the last tabFocus text.
  7119.              In such case, continue search.
  7120.             */
  7121.         }
  7122.         
  7123.         lo_TraverseElement(context, state, &(pCurrentFocus->pElement), forward);
  7124.     }
  7125.  
  7126.     return( FALSE );
  7127. }    /* LO_getNextTabableElement  */
  7128.  
  7129. /* NO_TAB_NAVIGATION */
  7130.  
  7131.  
  7132.  
  7133.