ERRORCODE TextFlow::adjust_words(DB_RECORD_NUMBER p_record, DB_RECORD_NUMBER f_record, CHARACTER_DELTA cdelta, WORD_DELTA_PTR wdelta) { ParagraphPtr paragraph; W_INDEX w_index; TEXT_WORD_PTR wp; ERRORCODE error; /* Redisplay vars */ LINE_PTR lp; FramePtr frame; PCOORD erase_xmin, erase_xmax; W_INDEX old_windex; L_INDEX l_index; BOOL deleted_something; /* // Initialize the word delta structure first. */ wdelta->w_start = -1; wdelta->count = 0; /* Make sure we avoid busy work. */ if (cdelta.count == 0) { return ERRORCODE_None; } /* Lock the paragraph so we can access it. */ if ((paragraph = (ParagraphPtr)database->get_record(p_record, &error, RECORD_TYPE_Paragraph)) == NULL) { return error; } /* Get the frame object. */ if ((frame = (FramePtr)database->get_record(f_record, &error, RECORD_TYPE_Frame)) == NULL) { paragraph->release(); return error; } /* // Run through the word array and update all words which need it. // We must be careful using a pointer into the word array data since any // deletions can cause the array data to change locations. */ /* Initialize redisplay variables */ deleted_something = FALSE; lp = NULL; old_windex = 0; l_index = frame->line_of_word(0, NULL); lp = (LINE_PTR)frame->line.get_element(l_index); for (wp = (TEXT_WORD_PTR)paragraph->word.get_element(0), w_index = 0; w_index < paragraph->word.count(); old_windex++, w_index++, wp++) { C_INDEX this_start; if ((this_start = wp->c_start) >= cdelta.c_start) { /* This has moved. Move it now. */ /* If deleting, see if the array is still valid. */ if ((this_start += cdelta.count) <= cdelta.c_start) { this_start = cdelta.c_start; if (!deleted_something) { deleted_something = TRUE; erase_xmin = wp->x_offset + wp->draw_left; erase_xmax = wp->x_offset + wp->draw_width; } /* // Update the word delta. // This makes the (valid) assumption that words which get // deleted are all contiguous. */ if (wdelta->w_start == -1) { wdelta->w_start = w_index; /* Start updating w1 (last word to erase) */ l_index = frame->line_of_word(w_index, NULL); lp = (LINE_PTR)frame->line.get_element(l_index); erase_xmax = __max(erase_xmax, wp->x_offset + wp->draw_width); erase_xmin = __min(erase_xmin, wp->x_offset + wp->draw_left); } else { /* The previous word has vanished. Delete it now. */ if (old_windex > lp->w_end) { /* That's all the words for this line. Erase the words */ add_width_refresh_extent(frame, lp, erase_xmin, erase_xmax, REFRESH_ERASE); /* Start a new line */ erase_xmin = wp->x_offset + wp->draw_left; erase_xmax = wp->x_offset + wp->draw_width; lp++; } else { erase_xmax = __max(erase_xmax, wp->x_offset + wp->draw_width); } wdelta->count--; /* Do the actual delete. */ paragraph->delete_word(--w_index); paragraph->modified(); #ifdef DEBUG_AW printf("Delete previous word (%d)\n", w_index); #endif /* Recompute 'wp' in case 'word.data' changed. */ wp = (TEXT_WORD_PTR)paragraph->word.get_element(w_index); } } #ifdef DEBUG_AW printf("Bumped WORD %d from index %d to %d (", w_index, wp->c_start, this_start); #endif /* Set the new start. */ wp->c_start = this_start; #ifdef DEBUG_AW dump_word(paragraph, wp); printf(")\n"); #endif } else { /* Advance the erase anchor point */ erase_xmin = __min(erase_xmin, wp->x_offset + wp->draw_left); } } if (deleted_something) { /* Found at least one line to delete. Delete the last line we found */ add_width_refresh_extent(frame, lp, erase_xmin, erase_xmax, REFRESH_ERASE); } /* Release the objects we got. */ frame->release(); paragraph->release(); return ERRORCODE_None; }
ERRORCODE TextFlow::rebuild_words(DB_RECORD_NUMBER p_record, DB_RECORD_NUMBER f_record, CHARACTER_RANGE crange, WORD_DELTA_PTR wdelta, WORD_RANGE_PTR wrange) { ParagraphPtr paragraph; FramePtr frame; W_INDEX w_index, w_start, w_end; TEXT_WORD_PTR wp; C_INDEX c_index; CHARACTER_PTR cp; ERRORCODE error; /* // Initialize the word delta structure first. */ wrange->w_start = wrange->w_end = wdelta->w_start = -1; wdelta->count = 0; /* Get the paragraph so we can operate on it. */ if ((paragraph = (ParagraphPtr)database->get_record(p_record, &error, RECORD_TYPE_Paragraph)) == NULL) { /* Error! */ return error; } /* See if there is any text. */ SHORT text_count = paragraph->number_of_characters(); if (text_count == 0) { /* No text! No words to form. */ paragraph->release(); return ERRORCODE_None; } /* Figure out where to begin forming words. */ if (crange.c_start < 0) { crange.c_start = 0; } if (crange.c_start >= text_count) { crange.c_start = text_count-1; } if (crange.c_end < 0 || crange.c_end >= text_count) { crange.c_end = text_count-1; } if (crange.c_start > crange.c_end) { /* // Huh? Bad parameters! // We could adjust the parameters to full extent and continue, but then // we might not catch a bad programming case. // Let's just return an error for now. */ od("rebuild_words: bad parameters\r\n"); paragraph->release(); return ERRORCODE_BadParameter; } /* // The first thing we need to do is go through and figure out which words // are going to be rebuilt. The first word to be rebuilt is the word containing // the start change (or the previous one if it's the first character of that // word). The c_index where word forming starts is the first character of // this word. The last word to be rebuilt is the word containing the end // change. The c_index where word forming stops is the last character of this // word. These words will be deleted and rebuilt from the c_index range // generated. */ /* // A paragraph should always have at least one word (the eop word). // OK. Here's the first problem: what happens if c_start > word(eop).c_start? // This means that the character indicies are inconsistent. This shouldn't // happen, so let's see if we can detect it and flag an error. */ /* // Look for the word containing the start offset. */ SHORT word_count = paragraph->number_of_words(); w_start = w_end = word_count; for (w_index = 0, wp = (TEXT_WORD_PTR)paragraph->word.get_element(0); w_index < word_count; wp++, w_index++) { if (wp->c_start >= crange.c_start) { w_start = w_index-1; break; } } if (w_start == word_count) { /* Couldn't find the c_index in our word array. Inconsistent. */ paragraph->release(); od("rebuild_words: start index inconsistent\r\n"); return ERRORCODE_BadParameter; } else if (w_start < 0) { /* Means c_start is in first word. */ w_start = 0; } #ifdef DEBUG_RW printf("Found start offset %d in word %d (", crange.c_start, w_start); dump_word(paragraph, (TEXT_WORD_PTR)paragraph->word.data + w_start); printf(")\n"); #endif /* Get the first index in this word. */ wrange->w_start = wrange->w_end = wdelta->w_start = w_start; if ((c_index = paragraph->get_word(w_start)->c_start) < crange.c_start) { /* Move back to start of word. */ crange.c_start = c_index; wdelta->w_start++; /* First word stays put. */ } #ifdef DEBUG_RW printf("Start offset moves to %d\n", crange.c_start); #endif /* // Look for the word containing the end offset. */ for (; w_index < word_count; wp++, w_index++) { if (wp->c_start >= crange.c_end) { w_end = w_index; break; } } if (w_end == word_count) { /* Couldn't find the c_index in our word array. Inconsistent. */ paragraph->release(); od("rebuild_words: end index inconsistent\r\n"); return ERRORCODE_IntError; } else if (w_end == word_count-1) { /* We ended up on the eop word. Move off of it. */ w_end--; } #ifdef DEBUG_RW printf("Found end offset %d in word %d (", crange.c_end, w_end); dump_word(paragraph, (TEXT_WORD_PTR)paragraph->word.data + w_end); printf(")\n"); #endif /* Get the first index of the next word. */ if ((c_index = paragraph->get_word(w_end+1)->c_start-1) > crange.c_end) { /* Move forward to end of word. */ crange.c_end = c_index; } #ifdef DEBUG_RW printf("End offset moves to %d\n", crange.c_end); #endif /* // Delete the existing range if we have one. // Perhaps this should be optimized someday to do all at once! */ if (w_end >= w_start) { /* Redisplay variables */ BOOL new_line = TRUE; L_INDEX l_index; LINE_PTR lp; TEXT_WORD_PTR wp0; PCOORD erase_xmin, erase_xmax; if ((frame = (FramePtr)database->get_record(f_record, &error, RECORD_TYPE_Frame)) == NULL) { paragraph->release(); return error; } /* Get line pointer for redisplay purposes */ l_index = frame->line_of_word(w_end, NULL); lp = frame->get_line(l_index); erase_xmin = 0x7fffffff; erase_xmax = -erase_xmin; wdelta->count = -(w_end - w_start + 1); while (w_end >= w_start) { #ifdef DEBUG_RW printf("Delete word %d\n", w_end); #endif wp0 = paragraph->get_word(w_end); if (!new_line && w_end < lp->w_start) { new_line = TRUE; add_width_refresh_extent(frame, lp, erase_xmin, erase_xmax, REFRESH_ERASE); lp--; } if (new_line) { new_line = FALSE; erase_xmin = wp0->x_offset + wp0->draw_left; erase_xmax = wp0->x_offset + wp0->draw_width; } else { erase_xmin = __min(erase_xmin, wp0->x_offset + wp0->draw_left); erase_xmax = __max(erase_xmax, wp0->x_offset + wp0->draw_width); } paragraph->delete_word(w_end--); } add_width_refresh_extent(frame, lp, erase_xmin, erase_xmax, REFRESH_ERASE); frame->release(); } /* // Start forming new words. */ for (w_index = w_start, c_index = crange.c_start, cp = paragraph->get_character(c_index); c_index <= crange.c_end; ) { TEXT_WORD word; /* Start this word. */ #ifdef DEBUG_RW printf(*cp < ' ' ? "[%d]" : "[%c]", *cp); #endif word.type = character_type(*cp++); word.flags = WORD_FLAG_needs_sizing; word.c_start = c_index++; word.x_offset = -1; word.width = 0; /* Accumulate the rest of the word. */ if (word.type == WORD_TYPE_solid) { /* Search for the next word start. */ while (c_index <= crange.c_end && character_type(*cp) == word.type) { #ifdef DEBUG_RW printf(*cp < ' ' ? "(%d)" : "(%c)", *cp); #endif cp++; c_index++; } } /* This is a whole new word. */ #ifdef DEBUG_RW printf(" ADD word %d, type %d, c (%d to %d) \n", w_index, word.type, word.c_start, c_index-1); #endif paragraph->insert_word(w_index++, &word); /* Another delta in the word array. */ wdelta->count++; wrange->w_end++; } /* Release the paragraph. */ paragraph->release(TRUE); return ERRORCODE_None; }
ERRORCODE TextFlow::refresh_frame(DB_RECORD_NUMBER f_record, CHARACTER_RANGE crange) { ERRORCODE error; DB_RECORD_NUMBER p_record; WORD_RANGE wrange; FramePtr frame; ParagraphPtr paragraph; LINE line; PBOX pbox; /* Get the frame. */ if ((frame = (FramePtr)database->get_record(f_record, &error, RECORD_TYPE_Frame)) == NULL) { return error; } p_record = frame->get_paragraph(); /* Get the paragraph of the frame. */ if ((paragraph = (ParagraphPtr)database->get_record(p_record, &error, RECORD_TYPE_Paragraph)) == NULL) { frame->release(); return error; } /* Fix the character range if special values were used. */ if (crange.c_start < 0) { crange.c_start = 0; } if (crange.c_end < 0) { crange.c_end = paragraph->number_of_characters(); } /* Get the word range. */ paragraph->crange_to_wrange(crange, &wrange); paragraph->release(); /* Get the top (and possibly bottom) line.*/ frame->line_of_word(wrange.w_start, &line); pbox.y0 = line.baseline - line.ascend; /* Now see if the words differ. If not, we must be on the same line. */ if (wrange.w_end != wrange.w_start) { /* Get the bottom line. */ frame->line_of_word(wrange.w_end, &line); } pbox.y1 = line.baseline + line.descend; /* Get the frame so we can get the bound. */ PBOX bound = frame->get_bound(); FLAGS object_flags = frame_object->get_flags(); pbox.x0 = bound.x0; pbox.x1 = bound.x1; if (object_flags & OBJECT_FLAG_yflipped) { PCOORD y0 = pbox.y0; pbox.y0 = bound.y1 - pbox.y1; pbox.y1 = bound.y1 - y0; } else { pbox.y0 += bound.y0; pbox.y1 += bound.y0; } frame->release(); /* Do the actual refresh add. */ database->do_refresh_notify(&pbox, REFRESH_ALL, NULL); return ERRORCODE_None; }