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; }