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; }
VOID TextFlow::position_lines(FramePtr frame, VERT_ALIGN_TYPE align_type) { PCOORD y_offset; L_INDEX l_index; LINE_PTR lp; PBOX refresh_offsets; PBOX bound = frame->get_bound(); PCOORD baseline; /* // Compute the height of all the lines so we can know how to align them. */ y_offset = 0; baseline = 0; SHORT line_count = frame->number_of_lines(); if (line_count == 0) { refresh_offsets.x0 = 0; refresh_offsets.x1 = 0; } else { PCOORD xmin, xmax; xmin = 0x7fffffff; xmax = -xmin; for (lp = frame->get_line(0), l_index = 0; l_index < line_count; lp++, l_index++) { xmin = __min(xmin, lp->refresh_xmin); xmax = __max(xmax, lp->refresh_xmax); if (l_index == 0) { baseline = y_offset + lp->ascend; } else { baseline = y_offset + lp->line_spacing - lp->descend; } y_offset = baseline + lp->descend; } refresh_offsets.x0 = xmin; refresh_offsets.x1 = xmax; } /* // Compute the offset value from the height of the text and the frame height. */ if ((y_offset = bound.y1 - bound.y0 - y_offset) > 0) { switch (align_type) { case ALIGN_top: { y_offset = 0; break; } case ALIGN_middle: { y_offset /= 2; break; } case ALIGN_bottom: default: { break; } } } else { /* Text too big for frame. Top align it. */ y_offset = 0; } refresh_offsets.y0 = y_offset; /* // We have the amount to offset. Do the offset now. */ for (lp = frame->get_line(0), l_index = 0; l_index < line_count; lp++, l_index++) { if (l_index == 0) { y_offset += lp->ascend; } else { y_offset += lp->line_spacing - lp->descend; } if (y_offset != lp->baseline) { if (lp->baseline != -1) { add_line_refresh_extent(frame, lp); } lp->baseline = y_offset; add_line_refresh_extent(frame, lp); } y_offset += lp->descend; } refresh_offsets.y1 = __min(y_offset, bound.y1 - bound.y0); frame->set_refresh_offsets(refresh_offsets); }
ERRORCODE TextFlow::rebuild_lines(FrameObjectPtr object, WORD_RANGE wrange) { ERRORCODE error; DB_RECORD_NUMBER f_record = object->get_frame_record(); FramePtr frame; DB_RECORD_NUMBER p_record; ParagraphPtr paragraph; W_INDEX w_index; TEXT_WORD_PTR wp; PCOORD line_top; LINE_PTR lp; L_INDEX l_index; PCOORD frame_extent, frame_left, tab_size; PCOORD baseline; BOOL use_white_space; FLAGS frame_flags; frame_object = object; /* // Text is flowed by placing text words into lines within a frame. // Words are flowed until the end is reached and then until words stop moving. */ if ((frame = (FramePtr)database->get_record(f_record, &error, RECORD_TYPE_Frame)) == NULL) { return error; } frame_flags = frame->get_flags(); /* // See if we need to do any stretching. */ if (frame_flags & (FRAME_FLAG_stretch_frame | FRAME_FLAG_stretch_text)) { if (frame_flags & FRAME_FLAG_stretch_frame) { SHORT error; /* Stretch the frame before flowing. */ if ((error = object->stretch_frame()) != 0) { frame->release(); if (error > 0) { error = ERRORCODE_None; } return (ERRORCODE)error; } /* Frame did not change size. Flow normally. */ } #if 0 else #else /* Always check (or check, too). */ if (frame_flags & FRAME_FLAG_stretch_text) #endif { /* Stretch the text before flowing. */ if ((error = frame->stretch_text()) != ERRORCODE_None) { frame->release(); return error; } /* Flow all the text. */ wrange.w_start = wrange.w_end = -1; } } p_record = frame->get_paragraph(); if ((paragraph = (ParagraphPtr)database->get_record(p_record, &error, RECORD_TYPE_Paragraph)) == NULL) { frame->release(); return error; } /* // Ascertain the true start and end of the text flow request. */ SHORT word_count = paragraph->number_of_words(); if (wrange.w_start < 0) { wrange.w_start = 0; } if (wrange.w_end < 0) { wrange.w_end = word_count-1; } if (wrange.w_start > wrange.w_end || wrange.w_end >= word_count) { /* Error of a parameter nature. */ paragraph->release(); frame->release(); return ERRORCODE_BadParameter; } /* // Find the line where the desired word starts. // If the word hasn't been flowed yet, it may be "between" existing lines. // If a word is not in a line, we'll shove it into the line just following it. // This should always be possible, because the EOP word should always be // flowed and in a line (and no word should come after it). // // We can do this really easily by just checking the last word in the line. // If we are before that line, we have our line. */ SHORT line_count = frame->number_of_lines(); for (lp = frame->get_line(0), l_index = 0; l_index < line_count; l_index++, lp++) { /* MH 4/25/93 this was "if (lp->w_end >= wrange.w_start)" */ if (lp->w_end + 1 >= wrange.w_start) { break; } } /* // Adjust w_start to the start of the line or our word, whichever is first. */ if (wrange.w_start > lp->w_start) { wrange.w_start = lp->w_start; } #ifdef DEBUG_RL printf("Flow words from %d to %d @ line %d\n", wrange.w_start, wrange.w_end, l_index); #endif /* // Now, we have the line (l_index) and the first word to flow (w_start) into // it. Let's do some flowin'! */ /* Compute the flowable extent and the size of a tab. */ TextStyleRef style = paragraph->get_style(); PBOX bound = frame->get_bound(); frame_left = style.get_left_margin(); frame_extent = bound.x1 - bound.x0 - (style.get_left_margin() + style.get_right_margin()); use_white_space = frame_flags & FRAME_FLAG_use_white_space; if ((tab_size = frame_extent / 16) == 0) { tab_size = 1; } /* Compute the current top of line. */ baseline = lp->baseline; line_top = baseline - lp->ascend; #ifdef DEBUG_RL printf("current baseline: %ld, ascend: %ld, line_top:%ld\n", baseline, lp->ascend, line_top); #endif for (w_index = wrange.w_start, wp = paragraph->get_word(w_index);; ) { PCOORD extent; /* Horizontal extent. */ PCOORD x_offset; /* Horizontal position. */ PCOORD solid_x_offset; LINE old_line; BOOL line_changed; #ifdef DEBUG_RL printf("(flow next line)\n"); #endif /* Remember the old line so we can tell if it moved. */ old_line = *lp; /* Start a new line. */ lp->w_start = w_index; /* Initialize the horizontal extent. */ extent = frame_extent; /* Plug in parameters for this line and update line_top. */ lp->ascend = lp->descend = lp->line_spacing = 0; baseline = line_top; /* Start us at the left margin. */ x_offset = 0; solid_x_offset = 0; /* Flow words into this line until done (possibly passing w_end). */ while (w_index < word_count) { FLAGS flags = wp->flags; /* Calculate tab width if necessary. */ if (wp->type == WORD_TYPE_tab) { wp->width = ((x_offset + tab_size)/tab_size)*tab_size - x_offset; } /* See if this word will fit in the row. */ #ifdef DEBUG_RL printf("flow word "); dump_word(paragraph, wp); printf("..."); #endif if (flags & WORD_FLAG_override) { /* Something is overridden. */ wp->flags &= ~WORD_FLAG_override; if (flags & WORD_FLAG_override_flow) { /* // A flow word! // Note that the flags have been cleared, so we won't keep // hitting this. */ break; } } else if (x_offset + wp->width > extent /* doesn't fit */ && w_index != lp->w_start /* not first word */ && !(wp->type == WORD_TYPE_space && ((wp-1)->type == WORD_TYPE_solid || (wp-1)->type == WORD_TYPE_macro)) && wp->width != 0) /* real word */ { #ifdef DEBUG_RL printf(" Move word to next line\n"); #endif break; } /* Update the horizontal offsets. */ x_offset += wp->width; if (use_white_space || wp->type == WORD_TYPE_solid || wp->type == WORD_TYPE_macro) { solid_x_offset = x_offset; } if (wp->ascend > lp->ascend) { #ifdef DEBUG_RL printf("Expand ascend from %ld to %ld\n", lp->ascend, wp->ascend); #endif baseline -= lp->ascend; baseline += (lp->ascend = wp->ascend); #ifdef DEBUG_RL printf("Baseline moves to %ld\n", baseline); #endif } if (wp->descend > lp->descend) { #ifdef DEBUG_RL printf("Expand descend from %ld to %ld\n", lp->descend, wp->descend); #endif lp->descend = wp->descend; } if (wp->line_spacing > lp->line_spacing) { lp->line_spacing = wp->line_spacing; } /* Add this word to the line. */ lp->w_end = w_index++; #ifdef DEBUG_RL printf(" Add word to line (x:%ld, w:%ld).\n", x_offset-wp->width, wp->width); #endif /* If we just placed a break word, this line is done. */ if (wp++->type == WORD_TYPE_break) { /* // If the previous word: // (a) was a "macro" word, // (b) started at the beginning of the line, and // (c) had zero width, // don't flow the break to the next line. */ BOOL fBreak = TRUE; if (w_index >= 2 && x_offset == 0) { TEXT_WORD_PTR wprev = wp-2; /* I make an assumption - x_offset == 0 --> width == 0. */ // if (wprev->width == 0) { if (*(paragraph->get_character(wprev->c_start)) >= MACRO_CHARACTER) { fBreak = FALSE; } } } if (fBreak) { #ifdef DEBUG_RL printf("(Breaking on BREAK word)\n"); #endif /* We need to break for this line. */ break; } } } #ifdef DEBUG_RL printf("[ line %d base:%ld, asc:%ld, dsc:%ld, w: %d to %d ]\n", l_index, baseline, lp->ascend, lp->descend, lp->w_start, lp->w_end); #endif /* // We now know all the words in this line. Handle the placement. */ /* First calculate the alignment offset. */ if ((x_offset = extent - solid_x_offset) > 0) { /* Some space to distribute. */ switch (style.get_line_alignment()) { case ALIGN_center: { x_offset /= 2; break; } case ALIGN_left: { x_offset = 0; break; } default: { break; } } } else { x_offset = 0; } /* Now set the positions of the words in this line. */ line_changed = FALSE; /* Cross your fingers. */ x_offset += style.get_left_margin(); { W_INDEX w_index = lp->w_start, w_end = lp->w_end; TEXT_WORD_PTR wp = paragraph->get_word(w_index); /* Redisplay variables */ TEXT_WORD_PTR first_word_change = NULL; TEXT_WORD_PTR last_word_change = NULL; PCOORD erase_xmin, erase_xmax; BOOL found_erase = FALSE; lp->refresh_xmin = 0x7fffffff; lp->refresh_xmax = -lp->refresh_xmin; while (w_index <= w_end) { if (x_offset != wp->x_offset || (wp->flags & WORD_FLAG_changed_size)) { /* A word moved. */ #ifdef DEBUG_RL printf("word %d changed from %ld to %ld\n", w_index, wp->x_offset, x_offset); #endif if (first_word_change == NULL) { first_word_change = wp; } if (!found_erase && wp->x_offset != -1 && w_index >= old_line.w_start && w_index <= old_line.w_end) { erase_xmin = wp->x_offset + wp->draw_left; erase_xmax = wp->x_offset + wp->draw_width; found_erase = TRUE; } last_word_change = wp; if (wp->x_offset != -1 && w_index >= old_line.w_start && w_index <= old_line.w_end) { erase_xmin = __min(erase_xmin, wp->x_offset + wp->draw_left); erase_xmax = __max(erase_xmax, wp->x_offset + wp->draw_width); } wp->x_offset = x_offset; //remove this to do word-wise refresh; restore it to do full line refresh // line_changed = TRUE; } wp->flags &= ~WORD_FLAG_changed_size; lp->refresh_xmin = __min(lp->refresh_xmin, wp->x_offset + wp->draw_left); lp->refresh_xmax = __max(lp->refresh_xmax, wp->x_offset + wp->draw_width); x_offset += wp++->width; w_index++; } if (first_word_change != NULL && old_line.baseline != -1) { /* Add a DRAW for every word that moved; erase moved words from their old positions. */ add_words_refresh_extent(frame, &old_line, first_word_change, last_word_change, REFRESH_DRAW); if (found_erase) { add_width_refresh_extent(frame, &old_line, erase_xmin, erase_xmax, REFRESH_ERASE); } } } /* See if the line changed. */ if (!line_changed) { /* Words stayed the same. See if the line is different somehow. */ /* Compare using the OLD_LINE structure. This allows us to ignore the changes in refresh_xmin, refresh_xmax. */ line_changed = (memcmp(&old_line, lp, sizeof(OLD_LINE)) != 0); if (line_changed) { lp->baseline = -1; } } if (line_changed) { #ifdef DEBUG_RL printf("Line %d changed\n", l_index); printf("old [%d to %d] vs. new [%d to %d]\n", old_line.w_start, old_line.w_end, lp->w_start, lp->w_end); #endif if (old_line.baseline != -1) { add_line_refresh_extent(frame, &old_line); } } else { #ifdef DEBUG_RL printf("Line %d stayed the same\n", l_index); #endif /* We can end now! */ if (w_index > wrange.w_end) { break; } } /* See if we're done. */ if (w_index >= word_count) { /* Run out of words. Finished. */ break; } /* // Move to a new line. // Add one if there are no more left. */ line_top = baseline + lp->descend; if (++l_index == frame->number_of_lines()) { /* Create a new dummy line. */ LINE line; #ifdef DEBUG_RL printf("(Create a new line)\n"); #endif line.w_start = line.w_end = 0; /* In case. */ line.baseline = -1; frame->insert_line(l_index, &line); lp = frame->get_line(l_index); } else { /* Add a refresh extent for the old line in case it's changing. */ #ifdef DEBUG_RL printf("(Move onto existing line)\n"); #endif lp++; } } #ifdef DEBUG_RL printf("Done (final l_index:%d, w_index:%d)...\n", l_index, w_index); #endif /* If we flowed all words, delete any trailing lines. */ if (w_index == word_count) { if (++l_index < frame->number_of_lines()) { while (l_index < frame->number_of_lines()) { #ifdef DEBUG_RL printf("{Delete line}"); #endif add_line_refresh_extent(frame, frame->get_line(l_index)); frame->delete_line(l_index); } #ifdef DEBUG_RL printf("\n"); #endif } } if (frame_flags & FRAME_FLAG_ystretch_frame) { position_lines(frame, style.get_vertical_alignment()); object->ystretch_frame(); } position_lines(frame, style.get_vertical_alignment()); /* // Release our objects. */ paragraph->release(TRUE); frame->release(TRUE); return ERRORCODE_None; }