Пример #1
0
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;
}