/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "xp.h" #include "layout.h" #include "intl_csi.h" #ifdef PROFILE #pragma profile on #endif #ifdef XP_MAC # ifdef XP_TRACE # undef XP_TRACE # endif # define XP_TRACE(X) #else #ifndef XP_TRACE # define XP_TRACE(X) fprintf X #endif #endif /* XP_MAC */ #ifdef TEST_16BIT #define XP_WIN16 #endif /* TEST_16BIT */ #define INTL_FIND 1 /* ftang */ #include "libi18n.h" /************************* * The following is to speed up case conversion * to allow faster checking of caseless equal among strings. *************************/ #ifdef NON_ASCII_STRINGS # define TOLOWER(x) (tolower((int)(x))) #else /* ASCII TABLE LOOKUP */ static unsigned char lower_lookup[256]={ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26, 27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50, 51,52,53,54,55,56,57,58,59,60,61,62,63,64, 97,98,99,100,101,102,103,104,105,106,107,108,109, 110,111,112,113,114,115,116,117,118,119,120,121,122, 91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110, 111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128, 129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146, 147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164, 165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182, 183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200, 201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218, 219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236, 237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254, 255}; # define TOLOWER(x) (lower_lookup[(unsigned int)(x)]) #endif /* NON_ASCII_STRINGS */ static void lo_next_character(MWContext *context, lo_DocState *state, LO_Element **ele_loc, int32 *pos, Bool forward) { INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); int16 win_csid = INTL_GetCSIWinCSID(c); LO_Element *eptr; int32 position; eptr = *ele_loc; position = *pos; /* * If our current element is text, we may be able to just * move inside of it. */ if (eptr->type == LO_TEXT) { if ((forward != FALSE)&& (position < (eptr->lo_text.text_len - 1))) { #ifdef INTL_FIND /* Add by ftang to provide international find */ position = INTL_NextCharIdxInText(win_csid, (unsigned char*)eptr->lo_text.text, position ); #else position++; #endif *ele_loc = eptr; *pos = position; return; } else if ((forward == FALSE)&&(position > 0)) { #ifdef INTL_FIND /* Add by ftang to provide international find */ position = INTL_PrevCharIdxInText(win_csid, (unsigned char*)eptr->lo_text.text, position ); #else position--; #endif *ele_loc = eptr; *pos = position; return; } } /* * If we didn't return above, we need to move to a new element. */ if (forward != FALSE) { /* * If no next element, see if this is a CELL we can * hop out of. */ if (eptr->lo_any.next == NULL) { int32 no_loop_id; no_loop_id = eptr->lo_any.ele_id; /* * Jump cell boundries if there is one * between here and the next element. */ eptr = lo_JumpCellWall(context, state, eptr); /* * If non-null eptr is the cell we were in, move * to the next cell/element. */ if (eptr != NULL) { eptr = eptr->lo_any.next; } /* * infinite loop prevention */ if ((eptr != NULL)&&(eptr->lo_any.ele_id <= no_loop_id)) { #ifdef DEBUG XP_TRACE(("Find loop avoidance 1\n")); #endif /* DEBUG */ eptr = NULL; } } else { eptr = eptr->lo_any.next; } } else { /* * If no previous element, see if this is a CELL we can * hop out of. */ if (eptr->lo_any.prev == NULL) { int32 no_loop_id; no_loop_id = eptr->lo_any.ele_id; /* * Jump cell boundries if there is one * between here and the previous element. */ eptr = lo_JumpCellWall(context, state, eptr); /* * If non-null eptr is the cell we were in, move * to the previous cell/element. */ if (eptr != NULL) { eptr = eptr->lo_any.prev; } /* * infinite loop prevention */ if ((eptr != NULL)&&(eptr->lo_any.ele_id >= no_loop_id)) { #ifdef DEBUG XP_TRACE(("Find loop avoidance 2\n")); #endif /* DEBUG */ eptr = NULL; } } else { eptr = eptr->lo_any.prev; } } while (eptr != NULL) { if (eptr->type == LO_LINEFEED) { break; } else if ((eptr->type == LO_TEXT)&&(eptr->lo_text.text != NULL)) { break; } else if (eptr->type == LO_CELL) { /* * When we walk onto a cell, we need * to walk into it if it isn't empty. */ if ((forward != FALSE)&& (eptr->lo_cell.cell_list != NULL)) { eptr = eptr->lo_cell.cell_list; continue; } else if ((forward == FALSE)&& (eptr->lo_cell.cell_list_end != NULL)) { eptr = eptr->lo_cell.cell_list_end; continue; } } /* * Move forward or back to the next element */ if (forward != FALSE) { /* * If no next element, see if this is a CELL we can * hop out of. */ if (eptr->lo_any.next == NULL) { int32 no_loop_id; no_loop_id = eptr->lo_any.ele_id; /* * Jump cell boundries if there is one * between here and the next element. */ eptr = lo_JumpCellWall(context, state, eptr); /* * If non-null eptr is the cell we were in, move * to the next cell/element. */ if (eptr != NULL) { eptr = eptr->lo_any.next; } /* * infinite loop prevention */ if ((eptr != NULL)&&( eptr->lo_any.ele_id <= no_loop_id)) { #ifdef DEBUG XP_TRACE(("Find loop avoidance 3\n")); #endif /* DEBUG */ eptr = NULL; } } else { eptr = eptr->lo_any.next; } } else { /* * If no previous element, see if this is a CELL we can * hop out of. */ if (eptr->lo_any.prev == NULL) { int32 no_loop_id; no_loop_id = eptr->lo_any.ele_id; /* * Jump cell boundries if there is one * between here and the previous element. */ eptr = lo_JumpCellWall(context, state, eptr); /* * If non-null eptr is the cell we were in, move * to the previous cell/element. */ if (eptr != NULL) { eptr = eptr->lo_any.prev; } /* * infinite loop prevention */ if ((eptr != NULL)&& (eptr->lo_any.ele_id >= no_loop_id)) { #ifdef DEBUG XP_TRACE(("Find loop avoidance 4\n")); #endif /* DEBUG */ eptr = NULL; } } else { eptr = eptr->lo_any.prev; } } } if (eptr == NULL) { *ele_loc = NULL; *pos = 0; } else if (eptr->type == LO_TEXT) { *ele_loc = eptr; if (forward != FALSE) { *pos = 0; } else { #ifdef INTL_FIND /* Add by ftang to provide international find */ if(eptr->lo_text.text_len == 0) position = 0; else position = INTL_PrevCharIdxInText(win_csid, (unsigned char*)eptr->lo_text.text, eptr->lo_text.text_len ); #else position = eptr->lo_text.text_len - 1; if (position < 0) { position = 0; } #endif *pos = position; } } else if (eptr->type == LO_LINEFEED) { *ele_loc = eptr; *pos = 0; } } static Bool lo_find_in_list(MWContext *context, lo_DocState *state, LO_Element *eptr, char *cmp_text, int32 len, int32 position, LO_Element **start_ele_loc, int32 *start_position, LO_Element **end_ele_loc, int32 *end_position, Bool use_case, Bool forward) { int32 cnt; LO_Element *start_element, *end_element; int32 start_pos, end_pos; INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context); int16 win_csid = INTL_GetCSIWinCSID(c); while (eptr != NULL) { Bool have_start, not_equal; int charlen = 1; have_start = FALSE; while ((eptr != NULL)&&(have_start == FALSE)) { unsigned char *tptr; unsigned char *str; switch (eptr->type) { #ifndef INTL_FIND char tchar; #endif case LO_TEXT: if (eptr->lo_text.text != NULL) { PA_LOCK(str, unsigned char *, eptr->lo_text.text); tptr = (unsigned char *)(str + position); #ifdef INTL_FIND if (use_case) have_start = INTL_MatchOneCaseChar(win_csid, (unsigned char*)cmp_text,tptr,&charlen); else have_start = INTL_MatchOneChar(win_csid, (unsigned char*)cmp_text,tptr,&charlen); #else if (use_case == FALSE) { tchar = TOLOWER(*tptr); } else { tchar = *tptr; } if (cmp_text[0] == tchar) { have_start = TRUE; } #endif PA_UNLOCK(eptr->lo_text.text); } break; case LO_LINEFEED: if (cmp_text[0] == ' ') { have_start = TRUE; } break; case LO_HRULE: case LO_FORM_ELE: case LO_BULLET: case LO_IMAGE: case LO_SUBDOC: case LO_TABLE: default: break; } if (have_start == FALSE) { lo_next_character(context, state, &eptr, &position, forward); } } if (have_start == FALSE) { return(FALSE); } start_element = eptr; start_pos = position; #ifdef INTL_FIND if (len == charlen) #else if (len == 1) #endif { end_element = eptr; end_pos = position; *start_ele_loc = start_element; *start_position = start_pos; *end_ele_loc = end_element; *end_position = end_pos; return(TRUE); } #ifdef INTL_FIND cnt = charlen; #else cnt = 1; #endif not_equal = FALSE; lo_next_character(context, state, &eptr, &position, TRUE); while ((eptr != NULL)&&(cnt < len)&&(not_equal == FALSE)) { unsigned char *tptr; /* this needs to be an unsigned quantity! chouck 3-Nov-94 */ char *str; switch (eptr->type) { #ifndef INTL_FIND char tchar; #endif case LO_TEXT: if (eptr->lo_text.text != NULL) { PA_LOCK(str, char *, eptr->lo_text.text); tptr = (unsigned char *)(str + position); #ifdef INTL_FIND if (use_case) not_equal = ! INTL_MatchOneCaseChar(win_csid,(unsigned char *) cmp_text+cnt,tptr,&charlen); else not_equal = ! INTL_MatchOneChar(win_csid,(unsigned char *) cmp_text+cnt,tptr,&charlen); #else if (use_case == FALSE) { /* this needs to be an unsigned quantity! chouck 3-Nov-94 */ tchar = TOLOWER(*tptr); } else { tchar = (char) *tptr; } if (tchar != cmp_text[cnt]) { not_equal = TRUE; } #endif PA_UNLOCK(eptr->lo_text.text); } break; case LO_LINEFEED: if (cmp_text[cnt] != ' ') { not_equal = TRUE; } break; case LO_HRULE: case LO_FORM_ELE: case LO_BULLET: case LO_IMAGE: case LO_SUBDOC: case LO_TABLE: default: break; } #ifdef INTL_FIND cnt += charlen; #else cnt++; #endif if ((not_equal == FALSE)&&(cnt < len)) { lo_next_character(context, state, &eptr, &position, TRUE); } } if ((cnt == len)&&(not_equal == FALSE)) { end_element = eptr; end_pos = position; *start_ele_loc = start_element; *start_position = start_pos; *end_ele_loc = end_element; *end_position = end_pos; return(TRUE); } eptr = start_element; position = start_pos; lo_next_character(context, state, &eptr, &position, forward); } return(FALSE); } static Bool lo_element_in_floating_cell(MWContext *context, lo_DocState *state, LO_Element *eptr) { LO_Element *cell; LO_Element *f_ptr; int32 x, y; int32 ret_x, ret_y; /* * Error test. */ if (eptr == NULL) { return(FALSE); } x = eptr->lo_any.x + eptr->lo_any.x_offset; y = eptr->lo_any.y + eptr->lo_any.y_offset; /* * This wouldn't work if the element had zero width, but since * we are assuming we are calling this on an element returned * as the result of a FIND operation, it must be a text item * with a non-zero width. */ cell = lo_XYToDocumentElement(context, state, x, y, TRUE, FALSE, TRUE, &ret_x, &ret_y); if ((cell == NULL)||(cell->type != LO_CELL)) { return(FALSE); } /* * Now look for this cell in the float list. */ f_ptr = state->float_list; while (f_ptr != NULL) { if (f_ptr == cell) { return(TRUE); } f_ptr = f_ptr->lo_any.next; } return(FALSE); } Bool LO_FindText(MWContext *context, char *text, LO_Element **start_ele_loc, int32 *start_position, LO_Element **end_ele_loc, int32 *end_position, Bool use_case, Bool forward) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; LO_Element *eptr; LO_Element *start_element, *end_element; int32 start_pos, end_pos; int32 position; int32 i, len; char *cmp_text; Bool start_at_top; Bool search_float_list; Bool ret_val; start_at_top = FALSE; search_float_list = TRUE; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return(FALSE); } state = top_state->doc_state; eptr = *start_ele_loc; position = *start_position; if (eptr == NULL) { start_at_top = TRUE; if (forward != FALSE) { if (state->line_num <= 1) { return(FALSE); } else { LO_Element **array; #ifdef XP_WIN16 { XP_Block *larray_array; XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array); state->line_array = larray_array[0]; XP_UNLOCK_BLOCK(state->larray_array); } #endif /* XP_WIN16 */ XP_LOCK_BLOCK(array, LO_Element **, state->line_array); eptr = array[0]; XP_UNLOCK_BLOCK(state->line_array); if (eptr == NULL) { return(FALSE); } } position = 0; } else { if (state->line_num <= 1) { return(FALSE); } else { eptr = state->end_last_line; if (eptr == NULL) { return(FALSE); } } position = 0; } } /* * Else, if we were passed a start position, we need to know if * that position is in a floating element in the margin, or is * in one of the normal document lines. If we starting in the * float list, we don't want to search it a second time. */ else if (lo_element_in_floating_cell(context, state, eptr)) { search_float_list = FALSE; } len = XP_STRLEN(text); cmp_text = (char*) XP_ALLOC(len + 1); if (cmp_text == NULL) { return(FALSE); } #ifdef INTL_FIND /* If we use INTL_FIND. Do the conversion for the cmp_text later in INTL_MatchOneChar() routine */ for (i=0; ifloat_list; position = 0; ret_val = lo_find_in_list(context, state, eptr, cmp_text, len, position, &start_element, &start_pos, &end_element, &end_pos, use_case, forward); if (ret_val != FALSE) { *start_ele_loc = start_element; *start_position = start_pos; *end_ele_loc = end_element; *end_position = end_pos; if (cmp_text != NULL) { XP_FREE(cmp_text); } return(TRUE); } } if (cmp_text != NULL) { XP_FREE(cmp_text); } return(FALSE); } static Bool lo_child_search(MWContext *context, MWContext **ret_context, char *text, LO_Element **start_ele_loc, int32 *start_position, LO_Element **end_ele_loc, int32 *end_position, Bool use_case, Bool forward) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; Bool ret_val; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return(FALSE); } state = top_state->doc_state; ret_val = FALSE; if (top_state->is_grid == FALSE) { ret_val = LO_FindText(context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); if (ret_val != FALSE) { *ret_context = context; } } else { lo_GridCellRec *cell_ptr; cell_ptr = top_state->the_grid->cell_list; while (cell_ptr != NULL) { if (cell_ptr->context != NULL) { /* * Always start at the beginning of * child docs. */ *start_ele_loc = NULL; *end_ele_loc = NULL; *start_position = 0; *end_position = 0; ret_val = lo_child_search(cell_ptr->context, ret_context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); if (ret_val != FALSE) { break; } } cell_ptr = cell_ptr->next; } } return(ret_val); } static Bool lo_sibling_search(MWContext *child, MWContext **ret_context, char *text, LO_Element **start_ele_loc, int32 *start_position, LO_Element **end_ele_loc, int32 *end_position, Bool use_case, Bool forward) { MWContext *parent; int32 doc_id; lo_TopState *top_state; Bool ret_val; lo_GridCellRec *cell_ptr; /* * We must have a grid parent */ parent = child->grid_parent; if (parent == NULL) { return(FALSE); } /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(parent); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return(FALSE); } /* * We must have a grid parent */ if (top_state->is_grid == FALSE) { return(FALSE); } ret_val = FALSE; /* * Find the sibling after the child */ cell_ptr = top_state->the_grid->cell_list; while (cell_ptr != NULL) { if (cell_ptr->context == child) { cell_ptr = cell_ptr->next; break; } cell_ptr = cell_ptr->next; } /* * Search all siblings (if any). */ while (cell_ptr != NULL) { if (cell_ptr->context != NULL) { /* * Always start at the beginning of sibling docs. */ *start_ele_loc = NULL; *end_ele_loc = NULL; *start_position = 0; *end_position = 0; ret_val = lo_child_search(cell_ptr->context, ret_context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); if (ret_val != FALSE) { break; } } cell_ptr = cell_ptr->next; } return(ret_val); } Bool LO_FindGridText(MWContext *context, MWContext **ret_context, char *text, LO_Element **start_ele_loc, int32 *start_position, LO_Element **end_ele_loc, int32 *end_position, Bool use_case, Bool forward) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; Bool ret_val; LO_Element *start_ele; LO_Element *end_ele; int32 start_pos; int32 end_pos; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return(FALSE); } state = top_state->doc_state; ret_val = FALSE; start_ele = *start_ele_loc; end_ele = *end_ele_loc; start_pos = *start_position; end_pos = *end_position; /* * If no starting context we start at the top level grid context. */ if (*ret_context == NULL) { MWContext *parent; /* * Find the top of the nested grid tree */ parent = context; while ((parent->is_grid_cell != FALSE)&& (parent->grid_parent != NULL)) { parent = parent->grid_parent; } /* * Search the tree completely. */ ret_val = lo_child_search(parent, ret_context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); } else { MWContext *child; /* * Search the starting context at the starting position. */ child = *ret_context; ret_val = lo_child_search(child, ret_context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); /* * If it wasn't in the starting context, walk up the tree * searching all siblings (after the parent sibling) * at each level. */ while ((ret_val == FALSE)&&(child->is_grid_cell != FALSE)&& (child->grid_parent != NULL)) { /* * Always start at the beginning of sibling docs. */ *start_ele_loc = NULL; *end_ele_loc = NULL; *start_position = 0; *end_position = 0; ret_val = lo_sibling_search(child, ret_context, text, start_ele_loc, start_position, end_ele_loc, end_position, use_case, forward); child = child->grid_parent; } } /* * If we didn't find it, restore starting values. */ if (ret_val == FALSE) { *start_ele_loc = start_ele; *end_ele_loc = end_ele; *start_position = start_pos; *end_position = end_pos; } return(ret_val); } void LO_SelectText(MWContext *context, LO_Element *start, int32 start_pos, LO_Element *end, int32 end_pos, int32 *x, int32 *y) { int32 doc_id; lo_TopState *top_state; lo_DocState *state; /* * Get the unique document ID, and retreive this * documents layout state. */ doc_id = XP_DOCID(context); top_state = lo_FetchTopState(doc_id); if ((top_state == NULL)||(top_state->doc_state == NULL)) { return; } state = top_state->doc_state; if ((start == NULL)||(end == NULL)) { return; } LO_HighlightSelection(context, FALSE); state->selection_start = start; state->selection_start_pos = start_pos; state->selection_end = end; state->selection_end_pos = end_pos; LO_HighlightSelection(context, TRUE); *x = (start->lo_any.x + state->base_x); *y = (start->lo_any.y + state->base_y); } #ifdef TEST_16BIT #undef XP_WIN16 #endif /* TEST_16BIT */ #ifdef PROFILE #pragma profile off #endif