1 #ifndef production
2 static char rcsId[]="$Header$";
3 #endif
4 /*****
5 * layout.c : XmHTML layout computation routines
6 *
7 * This file Version	$Revision$
8 *
9 * Creation date:		Thu Nov  6 01:35:46 GMT+0100 1997
10 * Last modification: 	$Date$
11 * By:					$Author$
12 * Current State:		$State$
13 *
14 * Author:				newt
15 *
16 * Copyright (C) 1994-1997 by Ripley Software Development
17 * All Rights Reserved
18 *
19 * This file is part of the XmHTML Widget Library
20 *
21 * This library is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Library General Public
23 * License as published by the Free Software Foundation; either
24 * version 2 of the License, or (at your option) any later version.
25 *
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
29 * Library General Public License for more details.
30 *
31 * You should have received a copy of the GNU Library General Public
32 * License along with this library; if not, write to the Free
33 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 *****/
36 /*****
37 * ChangeLog
38 * $Log$
39 * Revision 1.3  2012/03/01 17:56:31  ziad
40 * Cput
41 *
42 * Revision 1.2  2011/11/10 14:37:55  ziad
43 * Cput
44 *
45 * Revision 1.1  2011/06/30 16:10:38  rwcox
46 * Cadd
47 *
48 * Revision 1.2  1998/04/27 07:00:15  newt
49 * Lots of changes, most important: hugely improved table layout
50 *
51 * Revision 1.1  1998/04/04 06:27:23  newt
52 * Initial Revision
53 *
54 *****/
55 #ifdef HAVE_CONFIG_H
56 #include "config.h"
57 #endif
58 
59 #include <stdlib.h>
60 #include <stdio.h>
61 
62 #include "toolkit.h"
63 #include XmHTMLPrivateHeader
64 
65 /*** External Function Prototype Declarations ***/
66 
67 /*** Public Variable Declarations ***/
68 
69 /*** Private Datatype Declarations ****/
70 typedef XmHTMLWord** (*WordProc)(XmHTMLObjectTable, XmHTMLObjectTable, int*);
71 
72 /*****
73 * Object bounding box. Used for recursive layout computations in tables
74 * and text flowing around images.
75 *****/
76 typedef struct _PositionBox{
77 	Cardinal x;					/* absolute box upper left x position	*/
78 	Cardinal y;					/* absolute box upper left y position	*/
79 	int lmargin;				/* left margin							*/
80 	int rmargin;				/* right margin							*/
81 	int tmargin;				/* top margin							*/
82 	int bmargin;				/* bottom margin						*/
83 	int width;					/* absolute box width					*/
84 	int height;					/* absolute box height 					*/
85 	int min_width;				/* minimum box width					*/
86 	int min_height;				/* minimum box height					*/
87 	int left;					/* absolute left position				*/
88 	int right;					/* absolute right position				*/
89 	int idx;					/* index of cell using this box			*/
90 	int rowspan;				/* no of rows spanned by this box		*/
91 	int colspan;				/* no of cells spanned by this box		*/
92 }PositionBox;
93 
94 /*** Private Function Prototype Declarations ****/
95 static void SetText(XmHTMLWidget html, PositionBox *box,
96 	XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
97 	Boolean in_pre, Boolean precompute);
98 static void SetRule(XmHTMLWidget html, PositionBox *box,
99 	XmHTMLObjectTableElement data);
100 static void SetApplet(XmHTMLWidget html, PositionBox *box,
101 	XmHTMLObjectTableElement data);
102 static void SetBlock(XmHTMLWidget html, PositionBox *box,
103 	XmHTMLObjectTableElement data);
104 static void SetNone(XmHTMLWidget html, PositionBox *box,
105 	XmHTMLObjectTableElement data);
106 static void SetBullet(XmHTMLWidget html, PositionBox *box,
107 	XmHTMLObjectTableElement data);
108 static void SetBreak(XmHTMLWidget html, PositionBox *box,
109 	XmHTMLObjectTableElement data);
110 static XmHTMLObjectTableElement SetTable(XmHTMLWidget html,
111 	PositionBox *box, XmHTMLObjectTableElement data);
112 
113 /*****
114 * Layout computation routines
115 *****/
116 static void ComputeTextLayout(XmHTMLWidget html, PositionBox *box,
117 	XmHTMLWord **words, int nstart, int *nwords, Boolean last_line,
118 	Boolean precompute);
119 static void ComputeTextLayoutPre(XmHTMLWidget html, PositionBox *box,
120 	XmHTMLWord **words, int nstart, int *nwords, Boolean last_line);
121 
122 /*****
123 * Various helper routines
124 *****/
125 static XmHTMLWord **getWords(XmHTMLObjectTableElement start,
126 	XmHTMLObjectTableElement end, int *nwords);
127 
128 static XmHTMLWord **getWordsRtoL(XmHTMLObjectTableElement start,
129 	XmHTMLObjectTableElement end, int *nwords);
130 
131 static void JustifyText(XmHTMLWidget html, XmHTMLWord *words[],
132 	int word_start, int word_end, Dimension sw, int len, int line_len,
133 	int skip_id);
134 
135 static void CheckAlignment(XmHTMLWidget html, XmHTMLWord *words[],
136 	int word_start, int word_end, int sw, int line_len, Boolean last_line,
137 	int skip_id);
138 
139 static void CheckVerticalAlignment(XmHTMLWidget html, PositionBox *box,
140 	XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
141 	Alignment valign);
142 
143 static void AdjustBaseline(XmHTMLWord *base_obj, XmHTMLWord **words,
144 	int start, int end, int *lineheight, Boolean last_line,
145 	Boolean only_img);
146 
147 static void AdjustBaselinePre(XmHTMLWord *base_obj, XmHTMLWord **words,
148 	int start, int end, int *lineheight, Boolean last_line);
149 
150 static void PreComputeTableLayout(XmHTMLWidget html, PositionBox *parent,
151 	XmHTMLObjectTableElement obj_start, XmHTMLObjectTableElement obj_end);
152 
153 static void ComputeTableLayout(XmHTMLWidget html, PositionBox *box,
154 	XmHTMLObjectTableElement obj_start, XmHTMLObjectTableElement obj_end);
155 
156 static void FinalizeTextLayout(XmHTMLWord **words, int nwords, Boolean in_pre);
157 
158 static void CreateLineTable(XmHTMLWidget html);
159 
160 /*
161 * characters that must be flushed against a word. Can't use ispunct since
162 * that are all printable chars that are not a number or a letter.
163 */
164 #define IS_PUNCT(c) (c == '.' || c == ',' || c == ':' || c == ';' || \
165 	c == '!' || c == '?')
166 
167 /*** Private Variable Declarations ***/
168 static int line, last_text_line;
169 static int max_width;
170 static XmHTMLWord *baseline_obj;
171 static Boolean had_break;		/* indicates a paragraph had a break */
172 static XmHTMLWord** (*get_word_func)(XmHTMLObjectTableElement,
173 	XmHTMLObjectTableElement, int *);
174 static int curr_anchor, named_anchor;
175 
176 #ifdef DEBUG
177 static int lines_done;
178 static int total_iterations;
179 #endif
180 
181 #define STORE_ANCHOR(DATA) { \
182 	if(DATA->text_data & TEXT_ANCHOR) \
183 	{ \
184 		/* save anchor data */ \
185 		for(i = 0 ; i < DATA->n_words; i++) \
186 		{ \
187 			/* sanity check */ \
188 			if(curr_anchor == HTML_ATTR(anchor_words)) \
189 			{ \
190 				_XmHTMLWarning(__WFUNC__(html, "_XmHTMLpaint"), XMHTML_MSG_77, \
191 					"normal"); \
192 				curr_anchor--; \
193 			} \
194 			/* copy worddata and adjust y position */ \
195 			HTML_ATTR(anchors[curr_anchor]) = DATA->words[i]; \
196 			if(DATA->words[i].type == OBJ_IMG) \
197 				HTML_ATTR(anchors[curr_anchor].y) =  DATA->words[i].y; \
198 			else \
199 				HTML_ATTR(anchors[curr_anchor].y) =  \
200 					DATA->words[i].y - DATA->words[i].font->ascent; \
201 			curr_anchor++; \
202 		} \
203 	} \
204 	if(DATA->text_data & TEXT_ANCHOR_INTERN) \
205 	{ \
206 		/* save named anchor location */ \
207 		if(named_anchor == HTML_ATTR(num_named_anchors)) \
208 		{ \
209 			_XmHTMLWarning(__WFUNC__(html, "_XmHTMLpaint"), XMHTML_MSG_77, \
210 				"named"); \
211 			named_anchor--; \
212 		} \
213 		/* copy worddata and adjust y position */ \
214 		HTML_ATTR(named_anchors[named_anchor]) = *DATA; \
215 		named_anchor++; \
216 	} \
217 }
218 
219 /**********
220 ***** Layout Computation.
221 *****
222 ***** The running vertical coordinate specifies the upper left corner
223 ***** of the object to be drawn.
224 *****
225 ***** So to render each object on a line at the same baseline, the following
226 ***** conventions are used to determine the proper vertical coordinate for
227 ***** each object to be drawn:
228 *****
229 ***** Text blocks
230 *****	y-coordinate specifies the baseline origin of each text element to
231 *****	be drawn -> y-coordinate given by the running y-coordinate *plus*
232 *****	the ascent of the current font.
233 *****	XDrawString uses the object's x and y coordinates as the baseline
234 *****	origin for the text to be drawn.
235 ***** Images
236 *****	y-coordinate equals running y-coordinate.
237 *****	XCopyArea uses the object's x and y coordinates as the upper-left
238 *****	corner of the image to be drawn.
239 ***** Form and User-Defined elements
240 *****	y-coordinate equals running y-coordinate.
241 *****	Window position uses the object's x and y coordinates as the
242 *****	upper-left corner of the window to be displayed;
243 ***** Horizontal Rules
244 *****	running y-coordinate plus a single linefeed specifies the upper-left
245 *****	corner of the rule. Two linefeeds plus the height of the rule
246 *****	are added to the running y-coordinate.
247 *****
248 ***** The height of a line is determined by the highest object on a line,
249 ***** and the running y-coordinate is updated accordingly.
250 **********/
251 
252 /*****
253 * Name:			_XmHTMLComputeLayout
254 * Return Type:	void
255 * Description:	displays every formatted object on to the screen.
256 * In:
257 *	w:			Widget to display
258 * Returns:
259 *	nothing.
260 *****/
261 void
_XmHTMLComputeLayout(XmHTMLWidget html)262 _XmHTMLComputeLayout(XmHTMLWidget html)
263 {
264 	XmHTMLObjectTableElement temp, end;
265 	PositionBox box;
266 	int i;
267 
268 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout Start\n"));
269 
270 	HTML_ATTR(paint_start) = temp = HTML_ATTR(formatted);
271 	HTML_ATTR(paint_x) = 0;
272 	HTML_ATTR(paint_width) = HTML_ATTR(work_width) + HTML_ATTR(margin_width);
273 
274 	line = last_text_line = 0;
275 	baseline_obj = (XmHTMLWord*)NULL;
276 	max_width = 0;
277 	had_break = False;
278 	curr_anchor = 0, named_anchor = 0;
279 
280 	/*****
281 	* work_width is core width minus one horizontal margin.
282 	* Maximum useable width is core width minus two times the horizontal
283 	* margin.
284 	*****/
285 	box.x       = HTML_ATTR(margin_width);		/* initial margin		*/
286 	box.y       = HTML_ATTR(margin_height);		/* initial margin		*/
287 	box.lmargin = HTML_ATTR(margin_width);		/* absolute left margin	*/
288 	box.rmargin = HTML_ATTR(work_width);		/* absolute right margin*/
289 	box.width   = box.rmargin - box.lmargin;	/* absolute box width	*/
290 	box.height  = -1;							/* unknown height		*/
291 	box.tmargin = 0;							/* top margin			*/
292 	box.bmargin = HTML_ATTR(margin_height);		/* bottom margin		*/
293 	box.left    = box.lmargin;					/* initial left offset	*/
294 	box.right   = box.rmargin;					/* initial right offset	*/
295 
296 	/* select appropriate word collector */
297 	if(HTML_ATTR(string_direction) == XmSTRING_DIRECTION_R_TO_L)
298 		get_word_func = getWordsRtoL;
299 	else
300 		get_word_func = getWords;
301 
302 #ifdef DEBUG
303 	lines_done = 0;
304 	total_iterations = 0;
305 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout:\n"
306 		"\tCore offset: %ix%i\n"
307 		"\tmargins: width = %i, height = %i\n"
308 		"\twidget offset: %ix%i\n",
309 		CORE_ATTR(x), CORE_ATTR(y), box.lmargin, box.tmargin, box.x, box.y));
310 #endif
311 
312 	/* sanity check */
313 	if(temp == NULL)
314 		return;		/* fix 01/28/97-06, kdh */
315 
316 	_XmHTMLFullDebug(5, ("layout.c: _XmHTMLComputeLayout, x = %d, y = %d \n",
317 		box.x, box.y));
318 
319 	while(temp != HTML_ATTR(last_formatted))
320 	{
321 		switch(temp->object_type)
322 		{
323 			/*
324 			* To get a proper text layout, we need to do the layout for
325 			* whole blocks of text at a time.
326 			*/
327 			case OBJ_TEXT:
328 				_XmHTMLFullDebug(5, ("layout.c: OBJ_TEXT\n"));
329 
330 				for(end = temp; end->next->object_type == OBJ_TEXT;
331 					end = end->next);
332 
333 				/* go and do text layout */
334 				SetText(html, &box, temp, end->next, False, False);
335 
336 				for(; temp->object_type == OBJ_TEXT; temp = temp->next)
337 				{
338 					STORE_ANCHOR(temp);
339 				}
340 				/* back up one element */
341 				temp = end;
342 				break;
343 
344 			case OBJ_PRE_TEXT:
345 				_XmHTMLFullDebug(5, ("layout.c: OBJ_PRE_TEXT\n"));
346 
347 				for(end = temp; end->next->object_type == OBJ_PRE_TEXT;
348 					end = end->next);
349 
350 				my_assert(end != NULL);
351 
352 				/* go and do text layout */
353 				SetText(html, &box, temp, end->next, True, False);
354 
355 				for(; temp->object_type == OBJ_PRE_TEXT; temp = temp->next)
356 				{
357 					STORE_ANCHOR(temp);
358 				}
359 				/* back up one element */
360 				temp = end;
361 				break;
362 			case OBJ_BULLET:
363 				_XmHTMLFullDebug(5, ("layout.c: OBJ_BULLET\n"));
364 
365 				SetBullet(html, &box, temp);
366 				break;
367 			case OBJ_HRULE:
368 				_XmHTMLFullDebug(5, ("layout.c: OBJ_HRULE\n"));
369 
370 				SetRule(html, &box, temp);
371 				break;
372 			case OBJ_TABLE:
373 				_XmHTMLFullDebug(5, ("layout.c: OBJ_TABLE\n"));
374 
375 				end = SetTable(html, &box, temp);
376 
377 				/*****
378 				* Now store anchor data in this table. We can't do this
379 				* in the table layout routine as the (recursive) computation
380 				* routines for nested tables will repeatedly store anchor data.
381 				*****/
382 				for(; temp != end; temp = temp->next)
383 				{
384 					if(temp->object_type == OBJ_TEXT ||
385 						temp->object_type == OBJ_PRE_TEXT)
386 					{
387 						STORE_ANCHOR(temp);
388 					}
389 					/* empty named anchors can cause this */
390 					else if(temp->text_data & TEXT_ANCHOR_INTERN)
391 					{
392 						/* save named anchor location */
393 						HTML_ATTR(named_anchors[named_anchor]) = *temp;
394 						named_anchor++;
395 					}
396 				}
397 				SetBlock(html, &box, temp);
398 
399 				/* back up one element */
400 				temp = end->prev;
401 				break;
402 			case OBJ_TABLE_FRAME:
403 				_XmHTMLFullDebug(5, ("layout.c: OBJ_TABLE_FRAME\n"));
404 #ifdef DEBUG
405 				_XmHTMLWarning(__WFUNC__(html, "_XmHTMLComputeLayout"),
406 					"Invalid object OBJ_TABLE_FRAME! (debug)\n");
407 #endif
408 				break;
409 			case OBJ_APPLET:
410 				_XmHTMLFullDebug(5, ("layout.c: OBJ_APPLET\n"));
411 
412 				SetApplet(html, &box, temp);
413 				SetBreak(html, &box, temp);
414 				break;
415 			case OBJ_BLOCK:
416 				_XmHTMLFullDebug(5, ("layout.c: OBJ_BLOCK\n"));
417 
418 				SetBlock(html, &box, temp);
419 				SetBreak(html, &box, temp);
420 				break;
421 			case OBJ_NONE:
422 				_XmHTMLFullDebug(5, ("layout.c: OBJ_NONE\n"));
423 
424 				SetNone(html, &box, temp);
425 				/* empty named anchors can cause this */
426 				if(temp->text_data & TEXT_ANCHOR_INTERN)
427 				{
428 					/* save named anchor location */
429 					HTML_ATTR(named_anchors[named_anchor]) = *temp;
430 					named_anchor++;
431 				}
432 				break;
433 			default:
434 				_XmHTMLWarning(__WFUNC__(html, "_XmHTMLComputeLayout"),
435 					XMHTML_MSG_78);
436 		}
437 		/* end command for painting the first page */
438 		if((box.y - temp->height > HTML_ATTR(work_height)) ||
439 			(box.y > HTML_ATTR(work_height)))
440 			HTML_ATTR(paint_end) = temp;
441 		if(box.x > max_width)
442 			max_width = box.x;
443 		temp = temp->next;
444 		/* restore original box width */
445 		box.width  = box.rmargin - box.lmargin;
446 	}
447 	/*****
448 	* Now adjust width of the anchors.
449 	* If the current anchor word and the next are on the same line, and these
450 	* words belong to the same anchor, the width of the current anchor word
451 	* is adjusted so it will seem to be continue across the whole line when
452 	* the mouse pointer is moved over an anchor.
453 	* We can adjust the width field directly because the html.anchors field is
454 	* only used for anchor lookup, not for rendering.
455 	*****/
456 	for(i = 0 ; i < HTML_ATTR(anchor_words); i++)
457 			HTML_ATTR(anchors[i].x) = HTML_ATTR(anchors[i].self->x);
458 	for(i = 0 ; i < HTML_ATTR(anchor_words); i++)
459 	{
460 		if((HTML_ATTR(anchors[i].owner) == HTML_ATTR(anchors[i+1].owner)) &&
461 			(HTML_ATTR(anchors[i].line) == HTML_ATTR(anchors[i+1].line)))
462 		{
463 			HTML_ATTR(anchors[i].width) =
464 				HTML_ATTR(anchors[i+1].x) - HTML_ATTR(anchors[i].x) + 2;
465 		}
466 		my_assert(HTML_ATTR(anchors[i].base) != NULL);
467 	}
468 	/*****
469 	* store total height for this document. We add the marginheight and
470 	* font descent to get the text nicely centered.
471 	*****/
472 	HTML_ATTR(formatted_height) = box.y + HTML_ATTR(margin_height) +
473 		HTML_ATTR(default_font)->descent;
474 
475 	/* Preferred width for this document, includes horizontal margin once. */
476 	HTML_ATTR(formatted_width) = max_width;
477 
478 	/* store new maximum line number */
479 	HTML_ATTR(nlines) = line;
480 
481 	/*****
482 	* Never adjust top_line, scroll_x or scroll_y. This will make the
483 	* widget jump to the line in question and start drawing at the scroll_x
484 	* and scroll_y positions.
485 	*****/
486 
487 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout, x_max = %d, "
488 		"y_max = %d, total lines: %i.\n", HTML_ATTR(formatted_width),
489 		HTML_ATTR(formatted_height), line));
490 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout, stored %i/%i "
491 		"anchor words\n", curr_anchor, HTML_ATTR(anchor_words)));
492 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout, stored %i/%i "
493 		"named anchors \n", named_anchor, HTML_ATTR(num_named_anchors)));
494 
495 	/* now process any images with an alpha channel (if any) */
496 	if(HTML_ATTR(delayed_creation))
497 		_XmHTMLImageCheckDelayedCreation(html);
498 
499 #ifdef DEBUG
500 	/* prevent divide by zero */
501 	if(lines_done)
502 	{
503 		_XmHTMLDebug(5, ("outlining stats\n"));
504 		_XmHTMLDebug(5, ("\tlines done: %i\n", lines_done));
505 		_XmHTMLDebug(5, ("\ttotal iterations: %i\n", total_iterations));
506 		_XmHTMLDebug(5, ("\taverage iterations per line: %f\n",
507 			(float)(total_iterations/(float)lines_done)));
508 	}
509 #endif
510 
511 	CreateLineTable(html);
512 
513 	/* compute clipmask to use for scrolling forms */
514 	_XmHTMLFormCreateClipmask(html);
515 
516 	_XmHTMLDebug(5, ("layout.c: _XmHTMLComputeLayout End\n"));
517 	return;
518 }
519 
520 static void
CreateLineTable(XmHTMLWidget html)521 CreateLineTable(XmHTMLWidget html)
522 {
523 	int nl;
524 	XmHTMLObjectTableElement temp;
525 	XmHTMLLineTable *table;
526 #ifdef DEBUG
527 	int nused = 0;
528 #endif
529 
530 	if(HTML_ATTR(line_table))
531 		free(HTML_ATTR(line_table));
532 
533 	HTML_ATTR(line_table) = (XmHTMLLineTable*)NULL;
534 
535 	/* sanity */
536 	if(HTML_ATTR(nlines) == 0)
537 		return;
538 
539 	HTML_ATTR(line_table) =
540 		(XmHTMLLineTable*)calloc(HTML_ATTR(nlines)+1,sizeof(XmHTMLLineTable));
541 
542 	table = HTML_ATTR(line_table);
543 
544 	for(temp = HTML_ATTR(formatted); temp && temp != HTML_ATTR(last_formatted);
545 		temp = temp->next)
546 	{
547 		/* sanity */
548 		my_assert(temp->line < HTML_ATTR(nlines)+1);
549 
550 		if(table[temp->line].used == False)
551 		{
552 			nl = temp->line;
553 			table[nl].used  = True;
554 			table[nl].y     = temp->y;
555 			table[nl].start = temp;
556 #ifdef DEBUG
557 			nused++;		/* keep usage counter */
558 #endif
559 
560 			if(temp->n_words > 1 &&
561 				temp->words[0].line != temp->words[temp->n_words-1].line)
562 			{
563 				int k, wl;
564 				for(k = 0 ; k < temp->n_words; k++)
565 				{
566 					/* sanity */
567 					my_assert(temp->words[k].line < HTML_ATTR(nlines)+1);
568 
569 					if(table[temp->words[k].line].used == False)
570 					{
571 						wl = temp->words[k].line;
572 						table[wl].used  = True;
573 						table[wl].y     = temp->words[k].y;
574 						table[wl].start = temp;
575 						table[wl].end   = temp;
576 #ifdef DEBUG
577 						nused++;
578 #endif
579 
580 						while(k < temp->n_words && temp->words[k].line != wl)
581 							k++;
582 					}
583 				}
584 			}
585 
586 			/* skip all objects on the same line */
587 			while(temp->next != HTML_ATTR(last_formatted) &&
588 				nl == temp->next->line)
589 			{
590 				temp = temp->next;
591 				if(temp->n_words > 1 &&
592 					temp->words[0].line != temp->words[temp->n_words-1].line)
593 				{
594 					int k, wl;
595 
596 					for(k = 0 ; k < temp->n_words; k++)
597 					{
598 						/* sanity */
599 						my_assert(temp->words[k].line < HTML_ATTR(nlines)+1);
600 
601 						if(table[temp->words[k].line].used == False)
602 						{
603 							wl = temp->words[k].line;
604 							table[wl].used  = True;
605 							table[wl].y     = temp->words[k].y;
606 							table[wl].start = temp;
607 							table[wl].end   = temp;
608 
609 							while(k < temp->n_words &&
610 								temp->words[k].line != wl)
611 								k++;
612 #ifdef DEBUG
613 							nused++;
614 #endif
615 						}
616 					}
617 				}
618 			}
619 		}
620 	}
621 
622 	_XmHTMLDebug(5, ("layout.c: CreateLineTable, allocated %i lines and "
623 		"used %i of them\n", HTML_ATTR(nlines), nused));
624 }
625 
626 /*****
627 * Name: 		JustifyText
628 * Return Type: 	void
629 * Description: 	adjusts interword spacing to produce fully justified text.
630 *				justification is done on basis of the longest words.
631 * In:
632 *	start:		starting text element
633 *	end:		ending text element
634 *	w_start:	index in starting text element
635 *	w_end:		index in ending text element
636 *	sw:			width of a space in the current font
637 *	len:		current line length for this text
638 *	line_len:	maximum length of a line.
639 * Returns:
640 *	nothing, but *items contains updated delta fields to reflect the
641 *	required interword spacing.
642 * Note:
643 *	Words that start with a punctuation character are never adjusted,
644 *	they only get shoved to the right.
645 *	This routine could be much more efficient if the text to be justified
646 *	would be sorted.
647 *****/
648 static void
JustifyText(XmHTMLWidget html,XmHTMLWord * words[],int word_start,int word_end,Dimension sw,int len,int line_len,int skip_id)649 JustifyText(XmHTMLWidget html, XmHTMLWord *words[], int word_start,
650 	int word_end, Dimension sw, int len, int line_len, int skip_id)
651 {
652 	int word_len, longest_word = 0, nspace = 0, i, j, num_iter = 0;
653 
654 	/* See how many spaces we have to add */
655 	nspace = (int)((line_len - len)/(sw == 0 ? (sw = 3) : sw));
656 
657 	/*
658 	* last line of a block or no spaces to add. Don't adjust it.
659 	* nspace can be negative if there are words that are longer than
660 	* the available linewidth
661 	*/
662 	if(nspace < 1)
663 		return;
664 
665 	/* we need at least two words if we want this to work */
666 	if((word_end - word_start) < 2)
667 		return;
668 
669 	/* no hassling for a line with two words, fix 07/03/97-02, kdh */
670 	if((word_end - word_start) == 2)
671 	{
672 		/* just flush the second word to the right margin */
673 		words[word_start+1]->x += nspace*sw;
674 		return;
675 	}
676 
677 	/* pick up the longest word */
678 	for(i = word_start; i < word_end; i++)
679 	{
680 		if(i == skip_id)
681 			continue;
682 		if(words[i]->len > longest_word)
683 			longest_word = words[i]->len;
684 	}
685 
686 	word_len = longest_word;
687 
688 	/* adjust interword spacing until we run out of spaces to add */
689 	while(nspace && num_iter < XmHTML_MAX_JUSTIFY_ITERATIONS)
690 	{
691 		/* walk all words in search of the longest one */
692 		for(i = word_start ; i < word_end && nspace; i++, num_iter++)
693 		{
694 			if(i == skip_id || words[i]->len == 0)
695 				continue;
696 			/* Found! */
697 			if(words[i]->len == word_len &&
698 					!IS_PUNCT(*(words[i]->word)) &&
699 					!(words[i]->posbits & TEXT_SPACE_NONE))
700 			{
701 				/* see if we are allowed to shift this word */
702 				if(!(words[i]->posbits & TEXT_SPACE_TRAIL) &&
703 					!(words[i]->posbits & TEXT_SPACE_LEAD))
704 					continue;
705 
706 				/*****
707 				* Add a leading space if we may, but always shift all
708 				* following words to the right.
709 				*
710 				* fix 07/03/97-01, kdh
711 				******/
712 				if(words[i]->posbits & TEXT_SPACE_LEAD && i != word_start)
713 				{
714 					for(j = i; j < word_end; j++)
715 					{
716 						if(j == skip_id)
717 							continue;
718 						words[j]->x += sw;
719 					}
720 					nspace--;
721 				}
722 				if(nspace)
723 				{
724 					for(j = i + 1; j < word_end; j++)
725 					{
726 						if(j == skip_id)
727 							continue;
728 						words[j]->x += sw;
729 					}
730 
731 					/* we have only added a space if this is true */
732 					if(j != i+1)
733 						nspace--;
734 				}
735 			}
736 		}
737 		num_iter++;
738 		/* move on to next set of words eligible for space adjustement. */
739 		word_len = (word_len == 0 ? longest_word : word_len - 1);
740 	}
741 	if(num_iter == XmHTML_MAX_JUSTIFY_ITERATIONS)
742 	{
743 		_XmHTMLWarning(__WFUNC__(NULL, "JustifyText"),
744 			XMHTML_MSG_79, "Text justification", XmHTML_MAX_JUSTIFY_ITERATIONS,
745 			words[word_start]->owner->object->line);
746 	}
747 #ifdef DEBUG
748 	lines_done++;
749 	total_iterations += num_iter;
750 #endif
751 }
752 
753 /*****
754 * Name: 		CheckAlignment
755 * Return Type: 	void
756 * Description: 	adjusts x-position of every word to reflect requested
757 *				alignment.
758 * In:
759 *	w:			XmHTML widget
760 *	start:		starting text element
761 *	end:		ending text element
762 *	word_start:	starting word index in the start element.
763 *	sw:			current space width.
764 *	last_line:	indicates this is the last line in a text block;
765 * Returns:
766 *	nothing, but every word in start and end (and any object(s) in between
767 *	them) that belongs to the same line is updated to reflect the alignment.
768 *	This routine just returns if the current alignment matches the default
769 *	alignment.
770 *****/
771 static void
CheckAlignment(XmHTMLWidget html,XmHTMLWord * words[],int word_start,int word_end,int sw,int line_len,Boolean last_line,int skip_id)772 CheckAlignment(XmHTMLWidget html, XmHTMLWord *words[], int word_start,
773 	int word_end, int sw, int line_len, Boolean last_line, int skip_id)
774 {
775 	int i, width, offset;
776 
777 	/* sanity */
778 	if(word_end < 1)
779 		return;
780 
781 	/* total line width occupied by these words */
782 	width = words[word_end-1]->x + words[word_end-1]->width -
783 			words[word_start]->x;
784 
785 	_XmHTMLFullDebug(5, ("layout.c: CheckAlignment, start word: %s, index %i, "
786 		"end word: %s, index %i, width = %i, line length = %i\n",
787 		words[word_start]->word, word_start,
788 		words[word_end-1]->word, word_end-1, width, line_len));
789 
790 	switch(words[word_start]->owner->halign)
791 	{
792 		case XmHALIGN_RIGHT:
793 			offset = line_len - width;
794 			break;
795 		case XmHALIGN_CENTER:
796 			offset = (line_len - width)/2;
797 			break;
798 		case XmHALIGN_LEFT:		/* layout computation is always left-sided */
799 			offset = 0;
800 			break;
801 		case XmHALIGN_JUSTIFY:
802 			/* sw == -1 when used for <pre> text */
803 			if(HTML_ATTR(enable_outlining) && !last_line && sw != -1)
804 			{
805 				JustifyText(html, words, word_start, word_end, sw, width,
806 					line_len, (word_start < skip_id ? skip_id : -1));
807 				offset = 0;
808 				break;
809 			}
810 			/* fall thru */
811 		case XmHALIGN_NONE:
812 		default:
813 			/* use specified alignment */
814 			switch(HTML_ATTR(alignment))
815 			{
816 				case XmALIGNMENT_END:
817 					offset = line_len - width;
818 					break;
819 				case XmALIGNMENT_CENTER:
820 					offset = (line_len - width)/2;
821 					break;
822 				case XmALIGNMENT_BEGINNING:
823 				default:
824 					offset = 0;
825 					break;
826 			}
827 			break;
828 	}
829 	/*****
830 	* only adjust with a positive offset. A negative offset indicates
831 	* that the current width is larger than the available width.
832 	* Will ignore alignment setting for pre text that is wider than the
833 	* available window width.
834 	*****/
835 	if(offset <= 0)
836 		return;
837 	for(i = word_start; i < word_end; i++)
838 		words[i]->x += offset;
839 }
840 
841 /*****
842 * Name: 		getWords
843 * Return Type: 	XmHTMLWord**
844 * Description: 	creates an array containing all OBJ_TEXT elements between
845 *				start and end.
846 * In:
847 *	start:		element at which to start collecting words;
848 *	end:		element at which to end collecting words;
849 *	nwords:		no of words collected. Updated upon return;
850 * Returns:
851 *	an array of XmHTMLWord.
852 * Note:
853 *	This routine is used by the text layout routines to keep layout computation
854 *	managable.
855 *****/
856 static XmHTMLWord**
getWords(XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,int * nwords)857 getWords(XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
858 	int *nwords)
859 {
860 	static XmHTMLWord **words;
861 	XmHTMLObjectTableElement tmp;
862 	int i, k, cnt = 0;
863 
864 	for(tmp = start; tmp != end ; tmp = tmp->next)
865 		cnt += tmp->n_words;
866 
867 	words = (XmHTMLWord**)calloc(cnt, sizeof(XmHTMLWord*));
868 
869 	for(tmp = start, k = 0; tmp != end; tmp = tmp->next)
870 	{
871 		for(i = 0 ; i < tmp->n_words; i++)
872 		{
873 			/* store word ptr & reset position to zero */
874 			words[k] = &(tmp->words[i]);
875 			words[k]->x = 0;
876 			words[k]->y = 0;
877 			/* inherit spacing bits */
878 			words[k]->posbits = tmp->words[i].spacing;
879 			words[k]->line = 0;
880 
881 			/* reset baseline object */
882 			words[k++]->base = NULL;
883 		}
884 	}
885 
886 	*nwords = cnt;
887 	return(words);
888 }
889 
890 /*****
891 * Name: 		getWordsRtoL
892 * Return Type: 	XmHTMLWord**
893 * Description: 	creates an array containing all OBJ_TEXT elements between
894 *				start and end but reverses the object to properly accomodate
895 *				right-to-left layout.
896 * In:
897 *	start:		element at which to start collecting words;
898 *	end:		element at which to end collecting words;
899 *	nwords:		no of words collected. Updated upon return;
900 * Returns:
901 *	an array of XmHTMLWord.
902 * Note:
903 *	This is a seperate routine for performance reasons.
904 *****/
905 static XmHTMLWord**
getWordsRtoL(XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,int * nwords)906 getWordsRtoL(XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
907 	int *nwords)
908 {
909 	static XmHTMLWord **words;
910 	XmHTMLObjectTableElement tmp;
911 	int i, k, cnt = 0;
912 
913 	for(tmp = start; tmp != end ; tmp = tmp->next)
914 		cnt += tmp->n_words;
915 
916 	words = (XmHTMLWord**)calloc(cnt, sizeof(XmHTMLWord*));
917 
918 	/* sanity */
919 	if(end == NULL)
920 		for(end = start; end->next != NULL; end = end->next);
921 	for(tmp = end->prev, k = 0; tmp != start->prev; tmp = tmp->prev)
922 	{
923 		for(i = 0; i < tmp->n_words; i++)
924 		{
925 			/* store word ptr & reset position to zero */
926 			words[k] = &(tmp->words[i]);
927 			words[k]->x = 0;
928 			words[k]->y = 0;
929 			/* inherit spacing bits */
930 			words[k]->posbits = tmp->words[i].spacing;
931 			words[k]->line = 0;
932 
933 			/* reset baseline object */
934 			words[k++]->base = NULL;
935 		}
936 	}
937 	*nwords = cnt;
938 	return(words);
939 }
940 
941 /*****
942 * Name:			AdjustBaseline
943 * Return Type: 	void
944 * Description: 	adjusts the baseline for each word between start and end.
945 * In:
946 *	base_obj:	object which controls the baseline offset;
947 *	**words:	array of all words being laid out;
948 *	start:		starting word index;
949 *	end:		ending word index;
950 *	lineheight:	new lineheight (= spacing between to consecutive lines of text)
951 *	last_line:	True when called for the last line in paragraph. Used for
952 *				computing the proper vertical offset between the end of this
953 *				paragraph and the object following it;
954 *	only_img..:	True if current line only contains images. If it does, baseline
955 *				is adjusted to bottom of the image, otherwise baseline is
956 *				shifted downwards a bit (only for images which align at the
957 *				bottom).
958 * Returns:
959 *	nothing, but all words between start and end have their baseline adjusted.
960 *****/
961 static void
AdjustBaseline(XmHTMLWord * base_obj,XmHTMLWord ** words,int start,int end,int * lineheight,Boolean last_line,Boolean only_img)962 AdjustBaseline(XmHTMLWord *base_obj, XmHTMLWord **words, int start, int end,
963 	int *lineheight, Boolean last_line, Boolean only_img)
964 {
965 	int i, k, y_offset = 0;
966 
967 #ifdef NEW_LAYOUT
968 	int rl = lineheight;			/* running lineheight */
969 
970 	for(i = start; i < end ; i++)
971 	{
972 		switch(words[i]->type)
973 		{
974 			case OBJ_IMG:
975 				switch(words[i]->image->align)
976 				{
977 					case XmVALIGN_MIDDLE:
978 						y_offset = (*lineheight - words[i]->font->m_ascent)/2.;
979 
980 						/* adjust return value from SetText */
981 						/* fix 07/03/97-04, kdh */
982 						if(last_line && words[i] != words[end-1])
983 							*lineheight = y_offset;
984 						break;
985 
986 					case XmVALIGN_BASELINE:
987 					case XmVALIGN_BOTTOM:
988 						y_offset = *lineheight - words[i]->font->m_ascent;
989 						*lineheight += (only_img ?
990 										0 : words[i]->font->m_ascent/2.);
991 						break;
992 
993 					case XmVALIGN_TOP:
994 					default:
995 						break;
996 				}
997 				break;
998 			case OBJ_FORM:
999 				/* fix 07/04/97-01, kdh */
1000 				/* form elements are always aligned in the middle */
1001 				y_offset = (*lineheight + words[i]->font->m_ascent)/2.;
1002 
1003 				/* But they move the baseline down */
1004 				*lineheight += words[i]->font->m_ascent/2.;
1005 				break;
1006 
1007 			case OBJ_BLOCK:
1008 			default:
1009 				/* words are already at baseline */
1010 				y_offset = 0;
1011 
1012 				if(!last_line) /* sanity */
1013 					*lineheight = words[end]->height;
1014 				break;
1015 		}
1016 	}
1017 
1018 #else
1019 	my_assert(base_obj != NULL);
1020 
1021 	_XmHTMLDebug(5, ("layout.c: AdjustBaseline, lineheight IN: %i\n",
1022 		*lineheight));
1023 
1024 	switch(base_obj->type)
1025 	{
1026 		case OBJ_IMG:
1027 			switch(base_obj->image->align)
1028 			{
1029 				case XmVALIGN_MIDDLE:
1030 					y_offset = (*lineheight - base_obj->font->m_ascent)/2.;
1031 
1032 					/* adjust return value from SetText */
1033 					/* fix 07/03/97-04, kdh */
1034 					if(last_line && base_obj != words[end-1])
1035 						*lineheight = y_offset;
1036 					break;
1037 
1038 				case XmVALIGN_BASELINE:
1039 				case XmVALIGN_BOTTOM:
1040 					y_offset = *lineheight - base_obj->font->m_ascent;
1041 					*lineheight += (only_img ?
1042 									0 : base_obj->font->m_ascent/2.);
1043 					break;
1044 
1045 				case XmVALIGN_TOP:
1046 				default:
1047 					break;
1048 			}
1049 			break;
1050 		case OBJ_FORM:
1051 			/* fix 07/04/97-01, kdh */
1052 			/* form elements are always aligned in the middle */
1053 			y_offset = (*lineheight + base_obj->font->m_ascent)/2.;
1054 
1055 			/* But they move the baseline down */
1056 			*lineheight += base_obj->font->m_ascent/2.;
1057 			break;
1058 
1059 		case OBJ_BLOCK:
1060 		default:
1061 			/* words are already at baseline */
1062 			y_offset = 0;
1063 
1064 			if(!last_line) /* sanity */
1065 				*lineheight = words[end]->height;
1066 			break;
1067 	}
1068 
1069 	/*****
1070 	* Now adjust the baseline for every word on this line.
1071 	* Split into a y_offset and non y_offset part for performance reasons.
1072 	*****/
1073 	if(y_offset)
1074 	{
1075 		for(i = start; i < end; i++)
1076 		{
1077 			/* only move text objects */
1078 			if(words[i]->type == OBJ_TEXT)
1079 				words[i]->y += y_offset;
1080 			words[i]->base = base_obj;
1081 		}
1082 	}
1083 	else
1084 	{
1085 		for(i = start; i < end; i++)
1086 			words[i]->base = base_obj;
1087 	}
1088 
1089 	_XmHTMLDebug(5, ("layout.c: AdjustBaseline, lineheight OUT: %i\n",
1090 		*lineheight));
1091 #endif
1092 }
1093 
1094 /*****
1095 * Name:			FinalizeTextLayout
1096 * Return Type: 	void
1097 * Description: 	stores the final dimensions on the parents of the given
1098 *				words.
1099 * In:
1100 *	words:		array of words for which to update the parents
1101 *	nwords:		size of array.
1102 *	in_pre:		True if this is a chunk of <PRE> data.
1103 * Returns:
1104 *	nothing.
1105 *****/
1106 static void
FinalizeTextLayout(XmHTMLWord ** words,int nwords,Boolean in_pre)1107 FinalizeTextLayout(XmHTMLWord **words, int nwords, Boolean in_pre)
1108 {
1109 	int word_start, i;
1110 	XmHTMLObjectTableElement current = NULL;
1111 
1112 	/* Update all ObjectTable elements for these words */
1113 	current = NULL;
1114 	for(i = 0; i < nwords; i++)
1115 	{
1116 		if(current != words[i]->owner)
1117 		{
1118 			word_start = i;
1119 			current    = words[i]->owner;
1120 			current->x = words[i]->x;
1121 			current->width = words[i]->width;
1122 			current->line  = words[i]->line;
1123 			/*****
1124 			* To get correct screen updates, the vertical position and height
1125 			* of this object are that of the baseline object.
1126 			* The font is also changed to the font used by the baseline
1127 			* object.
1128 			*****/
1129 			current->y      = words[i]->base->y;
1130 			current->height = words[i]->base->height;
1131 			current->font   = words[i]->base->font;
1132 
1133 			/* get index of last word on the first line of this object. */
1134 			for(; i < word_start + current->n_words-1 &&
1135 				words[i]->line == words[i+1]->line; i++);
1136 			/*****
1137 			* Total line width is given by end position of last word on this
1138 			* line minus the starting position of the first word on this line.
1139 			* (ensures we take interword spacing into account)
1140 			*****/
1141 			current->width = words[i]->x + words[i]->width - current->x;
1142 
1143 			/*****
1144 			* Lineheight of this object is given by vertical position of last
1145 			* word minus vertical position of first word in this block. Only
1146 			* valid when this object spans multiple lines.
1147 			*****/
1148 			if(i != word_start + current->n_words-1)
1149 			{
1150 				current->height = words[word_start + current->n_words - 1]->y -
1151 					words[word_start]->y;
1152 			}
1153 			else if(in_pre && words[i]->base->spacing)
1154 			{
1155 				/* vertical line spacing in preformatted text */
1156 				current->height = ((int)words[i]->base->spacing) *
1157 						words[i]->base->font->height;
1158 			}
1159 			/* and set i to last word of this object */
1160 			i = word_start + current->n_words-1;
1161 			_XmHTMLDebug(5, ("layout.c: FinalizeTextLayout, object data: "
1162 				"x = %d, y = %d, width = %d, height = %d, line = %i\n",
1163 				current->x, current->y, current->width, current->height,
1164 				current->line));
1165 		}
1166 	}
1167 }
1168 
1169 /**********
1170 ***** This is the main text layout computation driver. It is used for all
1171 ***** preformatted text, ordinary paragraphs and layout computation
1172 ***** for text inside table cells.
1173 *****
1174 ***** For the latter, a special precompute mode is available.
1175 ***** In this mode, an estimate of the horizontal size of the cell is to be
1176 ***** made. To achieve this, the initial size of the textbox is set
1177 ***** to an unlimited value (linewidth will never be exceeded) and only
1178 ***** explicit linebreaks will be honored. After the layout routine
1179 ***** finishes, the estimed size of the (fully stretched) cell is returned
1180 ***** to the caller. The caller repeats this process for each cell in
1181 ***** a row. When the caller has all cell widths, it calculates the total
1182 ***** row width that would be required to give each cell it's maximum size.
1183 ***** When this row width fits in the (total) available width, each cell
1184 ***** is granted it's maximum size. When it doesn't fit, the caller
1185 ***** distributes the available width accross each cell, using the
1186 ***** maximum cell widths as a weighing factor.
1187 ***** When each cell has received it's final dimension, the caller calls
1188 ***** the cell layout routines once more, but this time in final layout
1189 ***** mode.
1190 ***** (Actually it is a bit more complex than this because the caller
1191 ***** precomputes all rows, then determines the widest cells in each
1192 ***** column and uses the widths of these cells to compute the final
1193 ***** column dimensions)
1194 **********/
1195 
1196 /*****
1197 * Name: 		SetText
1198 * Return Type: 	void
1199 * Description: 	main text layout driver;
1200 * In:
1201 *	html:		XmHTMLWidget id;
1202 *	*x:			initial x position, updated to new x position upon return;
1203 *	*y:			initial y position, updated to new y position upon return;
1204 *	start:		starting object id;
1205 *	end:		ending object id;
1206 *	in_pre:		True if called for preformatted text;
1207 *	precompute:	True if we are pre-computing the box dimensions (Tables!)
1208 * Returns:
1209 *	nothing
1210 *****/
1211 static void
SetText(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,Boolean in_pre,Boolean precompute)1212 SetText(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement start,
1213 	XmHTMLObjectTableElement end, Boolean in_pre, Boolean precompute)
1214 {
1215 	XmHTMLWord **words;
1216 	int nwords;
1217 	PositionBox my_box;
1218 
1219 	/*****
1220 	* to make it ourselves _much_ easier, put all the words starting from
1221 	* start and up to end in a single block of words.
1222 	*****/
1223 	words = get_word_func(start, end, &nwords);
1224 
1225 	/* sanity */
1226 	if(nwords == 0)
1227 		return;
1228 
1229 	_XmHTMLDebug(5, ("layout.c, SetText: initial box position: %i, %i\n",
1230 		box->x, box->y));
1231 	_XmHTMLDebug(5, ("layout.c, SetText: initial box dimensions: %i, %i\n",
1232 		box->width, box->height));
1233 
1234 	/*****
1235 	* Set up the initial PositionBox to be used for text layout.
1236 	*****/
1237 	my_box.x         = box->x;
1238 	my_box.y         = box->y;
1239 	my_box.lmargin   = box->lmargin;
1240 	my_box.rmargin   = box->rmargin;
1241 	my_box.left      = box->left;
1242 	my_box.right     = box->rmargin;
1243 	my_box.width     = my_box.right - my_box.left;
1244 	my_box.tmargin   = precompute ? 0 : box->tmargin;
1245 	my_box.min_width = -1;
1246 	my_box.height    = -1;
1247 
1248 	/* do text layout */
1249 	if(in_pre)
1250 		ComputeTextLayoutPre(html, &my_box, words, 0, &nwords, True);
1251 	else
1252 		ComputeTextLayout(html, &my_box, words, 0, &nwords, True, precompute);
1253 
1254 	_XmHTMLDebug(5, ("layout.c, SetText: updated box position: %i, %i\n",
1255 		my_box.x, my_box.y));
1256 	_XmHTMLDebug(5, ("layout.c, SetText: updated box dimensions: %i, %i\n",
1257 		my_box.width, my_box.height));
1258 
1259 	if(precompute)
1260 	{
1261 		/* update return values */
1262 		box->x = my_box.x;
1263 		box->y = my_box.y;
1264 		if(my_box.width > box->width || box->width == -1)
1265 			box->width = my_box.width;
1266 		if(my_box.min_width < box->min_width || box->min_width == -1)
1267 			box->min_width = my_box.min_width;
1268 		if(my_box.height > box->height || box->height == -1)
1269 			box->height = my_box.height;
1270 
1271 		/* no longer needed */
1272 		free(words);
1273 
1274 		/* done precomputing */
1275 		return;
1276 	}
1277 
1278 	/* Update all ObjectTable elements for these words */
1279 	FinalizeTextLayout(words, nwords, in_pre);
1280 
1281 	/* update return values */
1282 	box->x = my_box.x;
1283 	box->y = my_box.y;
1284 
1285 	/* free words */
1286 	free(words);
1287 }
1288 
1289 #define UPDATE_WORD(W) { \
1290 	/* images and forms need to have the font ascent substracted to get a */ \
1291 	/* proper vertical alignment. */ \
1292 	(W)->line = line; \
1293 	(W)->x = x_pos + e_space; \
1294 	if((W)->type != OBJ_TEXT && (W)->type != OBJ_BLOCK) \
1295 	{ \
1296 		have_object = True; \
1297 		(W)->y = y_pos + (W)->owner->y_offset; \
1298 	} else \
1299 		(W)->y = y_pos + (W)->owner->y_offset + base_obj->font->m_ascent; \
1300 	x_pos = (W)->x + (W)->width + (W)->owner->x_offset; \
1301 	_XmHTMLDebug(5, ("layout.c: UPDATE_WORD, word: %s, x = %i, " \
1302 		"y = %i\n", (W)->word, (W)->x, (W)->y)); \
1303 }
1304 
1305 /*****
1306 * Name: 		ComputeTextLayout
1307 * Return Type: 	void
1308 * Description: 	orders the given textdata into single lines, breaking and
1309 *				moving up to the next line if necessary.
1310 * In:
1311 *	w:			widget for which to do this;
1312 *	box:		bounding box to be used for computing text layout;
1313 *	words:		array of words to be laid out;
1314 *	nstart:		starting idx;
1315 *	nwords:		ending idx, can be updated upon return;
1316 *	last_line:	indicates that this routine is called for the the last line in
1317 *				a paragraph.
1318 *	precomp..:	True when *precomputing* cell layout (ignores linebreaks,
1319 *				unless they are explicit).
1320 * Returns:
1321 *	nothing
1322 * Note:
1323 *	This function does the layout of complete paragraphs at once.
1324 *	A paragraph is given by all text elements between start and end.
1325 *
1326 *	This is a rather complex routine. Things it does are the following:
1327 *	- considers images, HTML form members and text as the same objects;
1328 *	- adjusts baseline according to the highest object on a line;
1329 *	- adjusts space width if font changes;
1330 *	- performs horizontal alignment;
1331 *	- performs text outlining if required;
1332 *	- glues words together if required (interword spacing);
1333 *****/
1334 static void
ComputeTextLayout(XmHTMLWidget html,PositionBox * box,XmHTMLWord ** words,int nstart,int * nwords,Boolean last_line,Boolean precompute)1335 ComputeTextLayout(XmHTMLWidget html, PositionBox *box, XmHTMLWord **words,
1336 	int nstart, int *nwords, Boolean last_line, Boolean precompute)
1337 {
1338 	XmHTMLfont *basefont, *font;
1339 	XmHTMLWord *base_obj;
1340 	Cardinal x_pos, y_pos, x_start, y_start;
1341 	int i, k, sw, e_space = 0, word_start, word_width=0;
1342 	int lineheight = 0, p_height = 0;
1343 	Boolean have_object = False, first_line = True, done = False;
1344 	Boolean in_line = True, only_img = True;
1345 	Boolean need_baseline_adjustment = False;
1346 	int skip_id = -1, left, right, width, height;
1347 	int min_box_width = 0, max_box_width = 0, max_box_height;
1348 
1349 	/* initial offsets */
1350 	left    = box->left;
1351 	right   = box->right;
1352 	x_start = left;
1353 	x_pos   = x_start;
1354 	y_pos   = box->y + box->tmargin;
1355 	width   = box->width;
1356 	height  = box->height;
1357 
1358 	_XmHTMLDebug(5, ("layout.c: ComputeTextLayout, left = %i, right = %i, "
1359 		"width = %i, height = %i\n", left, right, width, height));
1360 
1361 	basefont = font = words[nstart]->font;
1362 	/* interword spacing */
1363 	e_space = sw = font->isp;
1364 
1365 	had_break = False;
1366 
1367 	/*****
1368 	* Proper baseline continuation of lines consisting of words with different
1369 	* properties (font, fontstyle, images, form members or anchors) require us
1370 	* to check if we are still on the same line. If we are, we use the baseline
1371 	* object of that line. If we are on a new line, we take the first word of
1372 	* this line as the baseline object.
1373 	*****/
1374 	if(!baseline_obj)
1375 		base_obj = words[nstart];
1376 	else
1377 		base_obj = (last_text_line == line ? baseline_obj : words[nstart]);
1378 
1379 	/* lineheight always comes from the current baseline object */
1380 	max_box_height = lineheight = base_obj->height;
1381 
1382 	word_start = nstart;
1383 
1384 	/*****
1385 	* Text layout:
1386 	* we keep walking words until we are about to exceed the available
1387 	* linewidth. When we are composing a line in this way, we keep track
1388 	* of the highest word (which will define the maximum lineheight).
1389 	* If a linefeed needs to be inserted, the lineheight is added to
1390 	* every word for a line. We then move to the next line (updating the
1391 	* vertical offset as we do) and the whole process repeats itself.
1392 	*****/
1393 	for(i = nstart; i < *nwords; i++)
1394 	{
1395 		/* skip everything if this is a newline */
1396 		if(words[i]->type == OBJ_BLOCK)
1397 			goto newline;
1398 
1399 		/*****
1400 		* We must flow text around a left-aligned image. First finish the
1401 		* the current line, then adjust the left margin and available
1402 		* linewidth and the height we should use.
1403 		* We can only honor this attribute if the width of this image is
1404 		* less than the available width.
1405 		* Multiple left/right aligned images aren't supported (yet).
1406 		*****/
1407 		else if(words[i]->type == OBJ_IMG)
1408 		{
1409 			if(words[i]->image->align == XmHALIGN_LEFT ||
1410 				words[i]->image->align == XmHALIGN_RIGHT)
1411 			{
1412 				if(skip_id == -1 && words[i]->width < width)
1413 				{
1414 					skip_id = i;
1415 					/* we are already busy with a line, finish it first */
1416 					if(in_line)
1417 						continue;
1418 					/* start of a line, just proceed */
1419 				}
1420 			}
1421 		}
1422 		else
1423 			only_img = False;
1424 
1425 		/* Non-text objects use a different approach of vertical alignment */
1426 		if(words[i]->type != OBJ_TEXT)
1427 			need_baseline_adjustment = True;
1428 
1429 		in_line = True;	/* we are busy with a line of text */
1430 		had_break = False;
1431 
1432 		/* get new space width if font changes */
1433 		if(font != words[i]->font)
1434 		{
1435 			font = words[i]->font;
1436 			sw = font->isp;		/* new interword spacing */
1437 
1438 			/*****
1439 			* If this font is larger than the current font it will become
1440 			* the baseline font for non-text objects.
1441 			*****/
1442 			if(font->lineheight > basefont->lineheight)
1443 				basefont = font;
1444 		}
1445 
1446 		/*****
1447 		* Sigh, need to check if we may break words before we do the
1448 		* check on current line width: if the current word doesn't have
1449 		* a trailing space, walk all words which don't have a leading
1450 		* and trailing space as well and end if we encounter the first word
1451 		* which *does* have a trailing space. We then use the total width
1452 		* of this word to check against available line width.
1453 		*****/
1454 		if(words[i]->type == OBJ_TEXT &&
1455 			!(words[i]->posbits & TEXT_SPACE_TRAIL) &&
1456 			i+1 < *nwords && !(words[i+1]->posbits & TEXT_SPACE_LEAD))
1457 		{
1458 			int j = i+1;
1459 			word_width = words[i]->width;
1460 			while(j < *nwords)
1461 			{
1462 #if 0
1463 				/* don't carry this along linebreaks or series of images */
1464 				if(words[j]->type == OBJ_BLOCK ||
1465 					(words[j]->type == OBJ_IMG &&
1466 						word_width + x_pos + e_space > right))
1467 					break;
1468 #endif
1469 				if(!(words[j]->posbits & TEXT_SPACE_LEAD))
1470 					word_width += words[j]->width;
1471 
1472 				/* see if this word has a trail space and the next a leading */
1473 				if(!(words[j]->posbits & TEXT_SPACE_TRAIL) &&
1474 					j+1 < *nwords && !(words[j+1]->posbits & TEXT_SPACE_LEAD))
1475 					j++;
1476 				else
1477 					break;
1478 			}
1479 		}
1480 		else
1481 			word_width = words[i]->width;
1482 
1483 		/* minimum box width must fit the longest non-breakable word */
1484 		if(min_box_width < word_width)
1485 			min_box_width = word_width;
1486 
1487 		_XmHTMLDebug(5, ("layout.c: ComputeTextLayout, word: %s, width = %i\n",
1488 			words[i]->word, word_width));
1489 
1490 newline:
1491 		/* Check if we are about to exceed the viewing width */
1492 		if((i && x_pos + word_width + e_space >= right) ||
1493 			words[i]->type == OBJ_BLOCK)
1494 		{
1495 			/*****
1496 			* If this is a forced linebreak we act as this is the last
1497 			* line in a paragraph: no implicit lineheight adjustment and no
1498 			* text justification in CheckAlignment.
1499 			*****/
1500 			Boolean is_break = words[i]->type == OBJ_BLOCK;
1501 
1502 			/*****
1503 			* Previous word (which marks the end of line) can't have a
1504 			* trailing space
1505 			*****/
1506 			if(i)
1507 				words[i-1]->posbits &= ~TEXT_SPACE_TRAIL;
1508 
1509 			/*****
1510 			* set font of non-text objects to the largest font of the
1511 			* text objects (required for proper anchor drawing)
1512 			*****/
1513 			if(base_obj->type != OBJ_TEXT)
1514 				base_obj->font = basefont;
1515 
1516 			/* adjust baseline for all words on the current line */
1517 			if(need_baseline_adjustment)
1518 				AdjustBaseline(base_obj, words, word_start, i, &lineheight,
1519 					is_break, only_img);
1520 			else	/* set baseline object */
1521 				for(k = word_start; k < i; k++)
1522 					words[k]->base = base_obj;
1523 
1524 			need_baseline_adjustment = False;
1525 
1526 			/* Adjust for alignment */
1527 			CheckAlignment(html, words, word_start, i, sw, width, is_break,
1528 				skip_id);
1529 
1530 			/* increment absolute height */
1531 			y_pos += lineheight;
1532 
1533 			/* increment absolute box height */
1534 			max_box_height += lineheight;
1535 
1536 			/* insert linebreak */
1537 			if(is_break)
1538 			{
1539 				/*****
1540 				* Additional vertical spacing to be inserted.
1541 				* For breaks, the line_data field specifies the no of
1542 				* newlines to be inserted. We only add this extra
1543 				* spacing if the total amount of vertical spacing
1544 				* to be inserted is larger than the current lineheight.
1545 				*****/
1546 				if(words[i]->line_data > 1)
1547 				{
1548 					int h;
1549 					h = (int)((words[i]->line_data)*base_obj->font->lineheight);
1550 
1551 					/* no negative linebreaks! */
1552 					if((h -= lineheight) < 0)
1553 						h = 0; /* no negative breaks! */
1554 					y_pos += h;
1555 					max_box_height += h;
1556 				}
1557 				/*****
1558 				* This word was a break and therefore the next word can't have
1559 				* a leading space (if it has it will mess up text
1560 				* justification).
1561 				* Fix 12/15/97-02, kdh
1562 				*****/
1563 				if(i+1 != *nwords)
1564 					words[i+1]->posbits &= ~TEXT_SPACE_LEAD;
1565 
1566 				/* just skip it */
1567 				e_space = 0;
1568 				UPDATE_WORD(words[i]);
1569 
1570 				/*****
1571 				* Next line starts on a new line, unless this is the last
1572 				* object in a line, then there is no line to start.
1573 				*****/
1574 				word_start  = i == *nwords - 1 ? i : i+1;
1575 
1576 				/* baseline object is the word itself */
1577 				words[i]->base = base_obj;
1578 
1579 				_XmHTMLFullDebug(5, ("layout.c: ComputeTextLayout, linefeed "
1580 					"(explicit break) x = %d, y = %d, lineheight = %i.\n",
1581 					x_pos, y_pos, lineheight));
1582 			}
1583 			else
1584 				word_start  = i;		/* next word starts on a new line */
1585 
1586 			/* update maximum box width */
1587 			if(x_pos - x_start > max_box_width)
1588 				max_box_width = x_pos - x_start;
1589 
1590 			x_pos = x_start;
1591 			line++;
1592 			lineheight  = words[i]->height;
1593 			base_obj    = words[i];
1594 			have_object = False;	/* object has been done */
1595 			first_line  = False;	/* no longer the first line */
1596 			in_line     = False;	/* done with current line */
1597 
1598 			_XmHTMLFullDebug(5, ("layout.c: ComputeTextLayout, linefeed, "
1599 				"x = %d, y = %d, lineheight = %i.\n", x_pos, y_pos,lineheight));
1600 
1601 			/* line is finished, set all margins for proper text flowing */
1602 			if(skip_id != -1)
1603 			{
1604 				/* start of text flowing */
1605 				if(height == -1)
1606 				{
1607 					/* save all info for this word */
1608 					words[skip_id]->line = line;
1609 					have_object = True;
1610 					words[skip_id]->y = y_pos +
1611 						words[skip_id]->owner->y_offset +
1612 						words[skip_id]->font->m_ascent;
1613 
1614 					/* this word sets the baseline for itself */
1615 					words[skip_id]->base = words[skip_id];
1616 
1617 					/* set appropriate margins */
1618 					if(words[skip_id]->image->align == XmHALIGN_RIGHT)
1619 					{
1620 						/* flush to the right margin */
1621 						words[skip_id]->x = right - words[skip_id]->width;
1622 						right = words[skip_id]->x;
1623 					}
1624 					else
1625 					{
1626 						/*****
1627 						* Flush to the left margin, it's the first word on
1628 						* this line, so no leading space is required.
1629 						*****/
1630 						words[skip_id]->x = x_pos;
1631 						x_pos = words[skip_id]->x + words[skip_id]->width;
1632 						left = x_pos + e_space;
1633 					}
1634 					p_height = 0;
1635 					height = words[skip_id]->height;
1636 					width = box->width - words[skip_id]->width - sw - e_space;
1637 				}
1638 				else /* increment height of this paragraph */
1639 					p_height += lineheight;
1640 
1641 				/*****
1642 				* If this is True, we are at the bottom of the image
1643 				* Restore margins and continue.
1644 				*****/
1645 				if(p_height >= height)
1646 				{
1647 					skip_id = -1;
1648 					height = -1;
1649 
1650 					left  = box->left;
1651 					right = box->right;
1652 					width = box->width;
1653 					x_pos = x_start;
1654 				}
1655 			}
1656 			only_img = True;
1657 
1658 			/* ignore remainder if this is a break */
1659 			if(is_break)
1660 				continue;
1661 		}
1662 
1663 		/* Check if lineheight should change */
1664 		if(lineheight < words[i]->height)
1665 		{
1666 			int k = word_start; /* fix 07/03/97-03, kdh */
1667 			int y_offset = 0;
1668 
1669 			/*****
1670 			* We need to shift the baseline of all words already placed
1671 			* downwards.
1672 			*****/
1673 			if(words[i]->type != OBJ_TEXT)
1674 			{
1675 				/*****
1676 				* Non-text objects have a meaningless font member. We only
1677 				* need to adjust the lineheight to get correct linespacing.
1678 				* Vertical alignment for non-text objects is performed in
1679 				* the AdjustBaseline routine.
1680 				*****/
1681 				lineheight = words[i]->height;
1682 				base_obj   = words[i];
1683 			}
1684 			else
1685 			{
1686 				/* save new lineheight and baseline object */
1687 				lineheight = words[i]->height;
1688 				base_obj   = words[i];
1689 
1690 				/* new vertical position */
1691 				y_offset = y_pos + base_obj->font->m_ascent;
1692 
1693 				/* shift 'em down. No need to check skip_id */
1694 				for(; k < i; k++)
1695 				{
1696 					if(words[k]->type == OBJ_TEXT)
1697 						words[k]->y = y_offset + words[k]->owner->y_offset;
1698 					else	/* non-text objects don't have a usefull font */
1699 						words[k]->y = y_pos + words[k]->owner->y_offset;
1700 				}
1701 			}
1702 		}
1703 
1704 		/*****
1705 		* Interword Spacing.
1706 		* 	1. word starts at beginning of a line, don't space it at all.
1707 		*	   (box->lmargin includes indentation as well)
1708 		* 	2. previous word does not have a trailing spacing:
1709 		* 		a. current word does have leading space, space it.
1710 		*		b. current word does not have a leading space, don't space it.
1711 		* 	3. previous word does have a trailing space:
1712 		*		a. always space current word.
1713 		* 	4. previous word does not have any spacing:
1714 		*		a. current word has leading space, space it.
1715 		*		b. current word does not have a leading space, don't space it.
1716 		* Note: if the previous word does not have a trailing space and the
1717 		*	current word does not have a leading space, these words are
1718 		*	``glued'' together.
1719 		*****/
1720 		e_space = 0;
1721 		if(i != 0 && x_pos != left)
1722 		{
1723 			if(!(words[i-1]->posbits & TEXT_SPACE_TRAIL))
1724 			{
1725 				if(words[i]->posbits & TEXT_SPACE_LEAD)
1726 					e_space = sw;
1727 			}
1728 			else if(words[i-1]->posbits & TEXT_SPACE_TRAIL)
1729 				e_space = sw;
1730 			else if(words[i]->posbits & TEXT_SPACE_LEAD)
1731 				e_space = sw;
1732 
1733 			/* additional end-of-line spacing? */
1734 			if(e_space && words[i]->len && words[i]->word[words[i]->len-1] == '.')
1735 				e_space += font->eol_sp;
1736 		}
1737 		/* no leading space if at left border */
1738 		else if (x_pos == left)
1739 			words[i]->posbits &= ~TEXT_SPACE_LEAD;
1740 
1741 		/*****
1742 		* save linenumber, x and y positions for this word or for
1743 		* multiple words needing to be ``glued'' together.
1744 		*****/
1745 		if(!(words[i]->posbits & TEXT_SPACE_TRAIL) &&
1746 			i+1 < *nwords && !(words[i+1]->posbits & TEXT_SPACE_LEAD) &&
1747 			words[i+1]->type == OBJ_TEXT)
1748 #if 0
1749 			words[i+1]->type != OBJ_BLOCK)
1750 #endif
1751 		{
1752 			/* first word must take spacing into account */
1753 			UPDATE_WORD(words[i]);
1754 			/* all other words are glued, so no spacing! */
1755 			e_space = 0;
1756 			i++;
1757 			while(i < *nwords)
1758 			{
1759 				/* don't take left/right flushed image into account */
1760 				if(i == skip_id)
1761 					continue;
1762 				/* connected word, save line, x and y pos. */
1763 				if(!(words[i]->posbits & TEXT_SPACE_LEAD))
1764 					UPDATE_WORD(words[i])
1765 
1766 				/*****
1767 				* Look ahead for leading spaces and explicit linebreaks.
1768 				* Presence of spacing and explicit linebreaks break the glue
1769 				* process.
1770 				*****/
1771 				if(!(words[i]->posbits & TEXT_SPACE_TRAIL) &&
1772 					i+1 < *nwords && !(words[i+1]->posbits & TEXT_SPACE_LEAD)
1773 					&& words[i+1]->type != OBJ_BLOCK)
1774 					i++;
1775 				else
1776 					break;
1777 			}
1778 		}
1779 		else /* save line, x and y pos for this word. */
1780 			UPDATE_WORD(words[i])
1781 
1782 		my_assert(base_obj != NULL);
1783 
1784 	}
1785 	/*****
1786 	* If we've got an image left, update it. We only have an image left if
1787 	* it's position hasn't been updated in the above loop, it will be
1788 	* positioned otherwise, but we ran out of text before we reached the
1789 	* box's height. So we need to update y_pos to move the baseline properly
1790 	* down. The box itself isn't restored as we have to check the alignment
1791 	* for this last line as well.
1792 	*****/
1793 	if(skip_id != -1)
1794 	{
1795 		if(words[skip_id]->x == 0 && words[skip_id]->y == 0)
1796 		{
1797 			UPDATE_WORD(words[skip_id]);
1798 		}
1799 		else	/* update y_pos */
1800 			y_pos += height - p_height;
1801 	}
1802 
1803 	/*****
1804 	* How do we know we are at the end of this block of text objects??
1805 	* If the calling routine set last_line to True, we know we are done
1806 	* and we can consider the layout computation done.
1807 	* If last_line is False, we can be sure that other text is coming so
1808 	* we must continue layout computation on the next call to this routine.
1809 	* If we haven't finished computing the layout for all words, we were
1810 	* flowing text around an object (currently only images), and we need
1811 	* to adjust the number of words done *and* be able to restart computation
1812 	* on the next call to this routine.
1813 	*****/
1814 	if(i == *nwords)
1815 	{
1816 		if(last_line)
1817 			done = True;
1818 		else
1819 			done = False;
1820 	}
1821 
1822 	/* also adjust baseline for the last line */
1823 	if(base_obj->type != OBJ_TEXT)
1824 		base_obj->font = basefont;
1825 
1826 	AdjustBaseline(base_obj, words, word_start, i, &lineheight, done, only_img);
1827 
1828 	/* also adjust alignment for the last line */
1829 	CheckAlignment(html, words, word_start, *nwords, sw, box->width, done,
1830 		skip_id);
1831 
1832 	/* save initial vertical offset */
1833 	y_start = box->y;
1834 
1835 	/* store box offsets */
1836 	box->y = y_pos;
1837 	box->x = x_pos;
1838 
1839 	/* store final box height */
1840 	if(first_line || (box->height = box->y - y_start) == 0)
1841 	{
1842 		if(lineheight > base_obj->font->lineheight)
1843 			box->height = lineheight;
1844 		else
1845 			box->height = base_obj->font->lineheight;
1846 	}
1847 	else
1848 		box->height = max_box_height;
1849 
1850 	/* special precomputing stuff */
1851 	if(precompute)
1852 	{
1853 		/*****
1854 		* Add font descent to height of box if the last line only contained
1855 		* text.
1856 		*****/
1857 		if(!have_object)
1858 			box->height += base_obj->font->m_ascent;
1859 		/*****
1860 		* Need to add an artificial linebreak if we haven't finished the
1861 		* current chunk of data.
1862 		*****/
1863 		if(in_line || word_start != *nwords)
1864 			box->y += lineheight;
1865 	}
1866 	else if(have_object)
1867 	{
1868 		box->y += lineheight; /* objects always cause a linebreak */
1869 		had_break = True;
1870 	}
1871 
1872 	/* check maximum box width again */
1873 	if(x_pos - x_start > max_box_width)
1874 		max_box_width = x_pos - x_start;
1875 
1876 	/* store minimum and maximum box width */
1877 	box->width = max_box_width > min_box_width ? max_box_width : min_box_width;
1878 	box->min_width = min_box_width;
1879 
1880 	/* why is this happening? */
1881 	if(base_obj->type == OBJ_TEXT)
1882 	{
1883 		box->width += base_obj->font->m_lbearing;
1884 		box->min_width += base_obj->font->m_lbearing;
1885 	}
1886 
1887 	/* and check against document maximum width */
1888 	if(max_box_width > max_width)
1889 		max_width = max_box_width;
1890 
1891 	/* last text line and baseline object for this piece of text */
1892 	last_text_line = line;
1893 	baseline_obj   = base_obj;
1894 
1895 	/*****
1896 	* If we haven't done a full line, we must increase linenumbering
1897 	* anyway as we've inserted a linebreak.
1898 	*****/
1899 	if(first_line)
1900 		line++;
1901 
1902 	_XmHTMLDebug(5, ("layout.c: ComputeTextLayout, returned box dimensions: "
1903 		"min_width = %i, max_width = %i\n", box->width, box->min_width));
1904 }
1905 
1906 /*****
1907 * Name:			AdjustBaselinePre
1908 * Return Type: 	void
1909 * Description: 	see AdjustBaseline
1910 * In:
1911 *
1912 * Returns:
1913 *	nothing.
1914 *****/
1915 static void
AdjustBaselinePre(XmHTMLWord * base_obj,XmHTMLWord ** words,int start,int end,int * lineheight,Boolean last_line)1916 AdjustBaselinePre(XmHTMLWord *base_obj, XmHTMLWord **words, int start, int end,
1917 	int *lineheight, Boolean last_line)
1918 {
1919 	int i, y_offset = 0;
1920 
1921 	switch(base_obj->type)
1922 	{
1923 
1924 		case OBJ_IMG:
1925 			switch(base_obj->image->align)
1926 			{
1927 				/******
1928 				* lineheight specifies the height of the image. All adjustments
1929 				* to be made are relative to this image.
1930 				******/
1931 
1932 				case XmVALIGN_MIDDLE:
1933 					y_offset = (*lineheight - base_obj->font->m_ascent)/2.;
1934 #if 0
1935 					y_offset = *lineheight/2;
1936 #endif
1937 
1938 					/* adjust return value from SetText */
1939 					/* fix 07/03/97-04, kdh */
1940 					if(last_line && base_obj != words[end-1])
1941 						*lineheight = y_offset;
1942 					break;
1943 
1944 				case XmVALIGN_BASELINE:
1945 				case XmVALIGN_BOTTOM:
1946 					y_offset = *lineheight ; /* + base_obj->font->m_ascent; */
1947 					*lineheight += 0.5*base_obj->font->m_ascent;
1948 					break;
1949 
1950 				case XmVALIGN_TOP:
1951 				default:
1952 					y_offset = -base_obj->font->m_ascent;
1953 					break;
1954 			}
1955 			break;
1956 		case OBJ_FORM:
1957 
1958 			/* fix 07/04/97-01, kdh */
1959 			/* form elements are always aligned in the middle */
1960 #if 0
1961 			y_offset = 0.5*(*lineheight + base_obj->font->m_ascent);
1962 #endif
1963 			y_offset = *lineheight/2;
1964 
1965 			/* But they move the baseline down */
1966 			*lineheight += base_obj->font->m_ascent/2;
1967 
1968 			break;
1969 		default:
1970 			/* everything is already at baseline */
1971 			break;
1972 	}
1973 
1974 	/* Now adjust the baseline offset for every word on this line. */
1975 	if(y_offset)
1976 	{
1977 		for(i = start; i < end; i++)
1978 		{
1979 			/* only move text objects */
1980 			if(words[i]->type == OBJ_TEXT)
1981 				words[i]->y += y_offset;
1982 		}
1983 	}
1984 }
1985 
1986 /*****
1987 * Name:			ComputeTextLayoutPre
1988 * Return Type: 	void
1989 * Description: 	main text layout engine for preformatted text.
1990 * In:
1991 *	html:		XmHTMLWidget id;
1992 *	box:		bounding box to be used for computing text layout;
1993 *	words:		array of words to be laid out;
1994 *	nstart:		starting idx;
1995 *	nwords:		ending idx, can be updated upon return;
1996 *	last_line:	indicates that this routine is called for the the last line in
1997 *				a paragraph.
1998 * Returns:
1999 *	nothing, but all words between start and end now have a screen position
2000 *	and a line number.
2001 *****/
2002 static void
ComputeTextLayoutPre(XmHTMLWidget html,PositionBox * box,XmHTMLWord ** words,int nstart,int * nwords,Boolean last_line)2003 ComputeTextLayoutPre(XmHTMLWidget html, PositionBox *box, XmHTMLWord **words,
2004 	int nstart, int *nwords, Boolean last_line)
2005 {
2006 	XmHTMLfont *basefont, *font;
2007 	XmHTMLWord *base_obj;
2008 	Cardinal x_pos, y_pos, y_start;
2009 	int i, word_start;
2010 	int lineheight = 0, y_offset, p_height = 0;
2011 	Boolean have_object = False, first_line = True, done = False;
2012 	int max_box_width = 0;
2013 
2014 	/* initial starting point */
2015 	x_pos  = box->left;
2016 	y_pos  = box->y;
2017 
2018 	/*****
2019 	* Baseline stuff. Always initialize to the first word of this para.
2020 	*****/
2021 	base_obj   = words[0];
2022 	basefont   = font = words[0]->font;
2023 	word_start = 0;
2024  	y_offset   = basefont->m_ascent;
2025 
2026 	/* initial lineheight equals height of the baseline object */
2027 	lineheight = base_obj->height;
2028 
2029 	/*****
2030 	* Text layout:
2031 	* we keep walking words until we are about to insert a newline.
2032 	* Newlines are marked by words width a non-zero spacing field.
2033 	* When we are composing a line in this way, we keep track
2034 	* of the highest word (which will define the maximum lineheight).
2035 	* If a linefeed needs to be inserted, the lineheight is added to
2036 	* every word for a line. We then move to the next line (updating the
2037 	* vertical offset as we do) and the whole process repeats itself.
2038 	*****/
2039 	for(i = nstart; i < *nwords && !done; i++)
2040 	{
2041 		/* compute new line spacing if font changes */
2042 		if(font != words[i]->font)
2043 		{
2044 			font = words[i]->font;
2045 
2046 			/*****
2047 			* If this font is larger than the current font it will become
2048 			* the baseline font for non-text objects.
2049 			* Must use maxbounds fontheight for fixed width fonts.
2050 			*****/
2051 			if(font->lineheight > basefont->lineheight)
2052 			{
2053 				basefont = font;
2054  				y_offset = basefont->m_ascent;
2055 				lineheight = basefont->lineheight;
2056 			}
2057 		}
2058 
2059 		/* check maximum lineheight */
2060 		if(lineheight < words[i]->height)
2061 		{
2062 			/* this becomes the new baseline object */
2063 			base_obj = words[i];
2064 
2065 			/*****
2066 			* Shift all words already placed on this line down. Don't do
2067 			* it for the first line in a paragraph and if this word is
2068 			* actually an image as this is already taken into account
2069 			* (paragraph spacing)
2070 			******/
2071 			if(!first_line && words[i]->type != OBJ_IMG)
2072 			{
2073 				/* fix 07/03/97-03, kdh */
2074 				int k = word_start;	/* idx of first word on this line */
2075 
2076 				/* new vertical position of all words in the current line */
2077 				y_pos += (words[i]->height - lineheight);
2078 
2079 				/* shift 'em down */
2080 				for(; k < i; k++)
2081 				{
2082 					words[k]->y = y_pos;
2083 					words[k]->base = base_obj;
2084 				}
2085 			}
2086 			/* Store new lineheight */
2087 			lineheight = words[i]->height;
2088 		}
2089 
2090 		/*****
2091 		* save line, x and y pos for this word.
2092 		* We don't do any interword spacing for <PRE> objects, they
2093 		* already have it. Images and forms need to have the font ascent
2094 		* substracted to get a proper vertical alignment.
2095 		*****/
2096 		words[i]->line = line;	/* fix 04/26/97-01, kdh */
2097 		words[i]->x    = x_pos;
2098 		words[i]->base = base_obj;
2099 		words[i]->y    = y_pos + words[i]->owner->y_offset +
2100 							words[i]->font->m_ascent;
2101 
2102 		/*****
2103 		* Required for proper baseline adjustment of the last line in this
2104 		* paragraph: non-text objects move the baseline downward.
2105 		******/
2106 		if(words[i]->type != OBJ_TEXT)
2107 			have_object = True;
2108 
2109 		x_pos += words[i]->width;
2110 
2111 		/* we must insert a newline */
2112 		if(words[i]->spacing != 0)
2113 		{
2114 			int y_shift = lineheight;
2115 
2116 			/*****
2117 			* Adjust font of non-text objects to the largest font of the
2118 			* text objects (required for proper anchor drawing)
2119 			*****/
2120 			if(base_obj->type != OBJ_TEXT)
2121 				base_obj->font = basefont;
2122 
2123 			/*****
2124 			* Adjust baseline for all words on the current line.
2125 			*****/
2126 			AdjustBaselinePre(base_obj, words, word_start, i+1, &y_shift,
2127 					False);
2128 
2129 			/*****
2130 			* If this is a textblock, set vertical adjustment to the
2131 			* no of newlines to add.
2132 			*****/
2133 			if(base_obj->type == OBJ_TEXT)
2134 				y_shift = (((int)words[i]->spacing) * y_offset);
2135 
2136 			y_pos += y_shift;
2137 
2138 			/* increment height of this paragraph */
2139 			p_height += y_shift;
2140 
2141 			/* Adjust for alignment */
2142 			CheckAlignment(html, words, word_start, i, -1, box->width, False,
2143 				-1);
2144 
2145 			if(x_pos > max_box_width)
2146 				max_box_width = x_pos;
2147 
2148 			x_pos = box->left;
2149 			line++;
2150 			word_start  = i+1;		/* next word starts on a new line */
2151 			base_obj    = words[i];
2152 			basefont    = base_obj->font;
2153  			y_offset    = basefont->m_ascent;
2154 			lineheight  = basefont->lineheight;
2155 			have_object = False;
2156 			first_line  = False;
2157 
2158 			_XmHTMLFullDebug(5, ("layout.c: ComputeTextLayoutPre, linefeed, "
2159 				"x = %d, y = %d.\n", x_pos, y_pos));
2160 
2161 			if(box->height != -1 && p_height >= box->height)
2162 				done = True;
2163 		}
2164 	}
2165 	/* sanity, can be true for short <pre></pre> paragraphs. */
2166 	if(word_start == *nwords)
2167 		word_start--;
2168 
2169 	if(i == *nwords)
2170 	{
2171 		if(last_line)
2172 			done = True;
2173 		else
2174 			done = False;
2175 	}
2176 	else if(done)
2177 	{
2178 		*nwords = i;
2179 		done = False;
2180 	}
2181 
2182 	/* also adjust baseline for the last line */
2183 	AdjustBaselinePre(base_obj, words,
2184 		(word_start == nstart ? nstart : word_start - 1), i, &lineheight, done);
2185 
2186 	/* also adjust alignment for the last line */
2187 	CheckAlignment(html, words, word_start, *nwords, -1, box->width, done, -1);
2188 
2189 	y_start = box->y;
2190 
2191 	/* non-text objects (images & form members) move the baseline downward */
2192 	if(have_object)
2193 	{
2194 		box->y = y_pos + lineheight;
2195 		had_break = True;
2196 	}
2197 	else
2198 		box->y = y_pos;
2199 	box->x = x_pos;
2200 
2201 	/* store final box height */
2202 	if((box->height = box->y - y_start) == 0)
2203 		box->height = lineheight;
2204 
2205 	/* check maximum box width again */
2206 	if(x_pos > max_box_width)
2207 		max_box_width = x_pos;
2208 
2209 	/*
2210 	* store box width. Minimum width is same as maximum width for
2211 	* preformatted text.
2212 	*/
2213 	box->width = box->min_width = max_box_width;
2214 
2215 	/* and check against document maximum width */
2216 	if(max_box_width > max_width)
2217 		max_width = max_box_width;
2218 
2219 	/* make sure we have a linefeed */
2220 	if(first_line)
2221 		line++;
2222 
2223 	/* all done */
2224 }
2225 
2226 /*****
2227 * Name: 		SetApplet
2228 * Return Type: 	inserts a dummy applet marker.
2229 * Description:
2230 * In:
2231 *	html:		XmHTMLWidget id;
2232 *	box:		current minipage;
2233 *	data:		element data.
2234 * Returns:
2235 *	nothing.
2236 *****/
2237 static void
SetApplet(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2238 SetApplet(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2239 {
2240 	XmHTMLfont *font = (data->font ? data->font : HTML_ATTR(default_font));
2241 
2242 	_XmHTMLDebug(5, ("layout.c, SetApplet: initial box position: %i, %i\n",
2243 		box->x, box->y));
2244 	_XmHTMLDebug(5, ("layout.c, SetApplet: initial box dimensions: %i, %i\n",
2245 		box->width, box->height));
2246 
2247 	data->x = box->x;
2248 	data->y = box->y + font->m_ascent;
2249 	data->height = font->lineheight;
2250 	data->line   = line;
2251 	box->y += data->height;
2252 
2253 	_XmHTMLDebug(5, ("layout.c, SetApplet: updated box position: %i, %i\n",
2254 		box->x, box->y));
2255 	_XmHTMLDebug(5, ("layout.c, SetApplet: updated box dimensions: %i, %i\n",
2256 		box->width, box->height));
2257 }
2258 
2259 /*****
2260 * Name: 		SetBlock
2261 * Return Type: 	void
2262 * Description: 	inserts a block marker (which does have a height)
2263 * In:
2264 *	html:		XmHTMLWidget id;
2265 *	box:		current minipage;
2266 *	data:		element data.
2267 * Returns:
2268 *	nothing.
2269 *****/
2270 static void
SetBlock(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2271 SetBlock(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2272 {
2273 	_XmHTMLDebug(5, ("layout.c, SetBlock: initial box position: %i, %i\n",
2274 		box->x, box->y));
2275 	_XmHTMLDebug(5, ("layout.c, SetBlock: initial box dimensions: %i, %i\n",
2276 		box->width, box->height));
2277 
2278 	data->x = box->x;
2279 	data->y = box->y + data->font->m_ascent;
2280 	data->height = 0;
2281 	data->line   = line;
2282 
2283 	_XmHTMLDebug(5, ("layout.c, SetBlock: updated box position: %i, %i\n",
2284 		box->x, box->y));
2285 	_XmHTMLDebug(5, ("layout.c, SetBlock: updated box dimensions: %i, %i\n",
2286 		box->width, box->height));
2287 }
2288 
2289 /*****
2290 * Name: 		SetNone
2291 * Return Type: 	void
2292 * Description: 	inserts a dummy marker (which doesn't have a height)
2293 * In:
2294 *	html:		XmHTMLWidget id;
2295 *	box:		current minipage;
2296 *	data:		element data.
2297 * Returns:
2298 *	nothing.
2299 *****/
2300 static void
SetNone(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2301 SetNone(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2302 {
2303 	_XmHTMLDebug(5, ("layout.c, SetNone: initial box position: %i, %i\n",
2304 		box->x, box->y));
2305 	_XmHTMLDebug(5, ("layout.c, SetNone: initial box dimensions: %i, %i\n",
2306 		box->width, box->height));
2307 
2308 	data->x = box->x;
2309 	data->y = box->y;
2310 	data->height = 0;
2311 	data->line   = line;
2312 
2313 	_XmHTMLDebug(5, ("layout.c, SetNone: updated box position: %i, %i\n",
2314 		box->x, box->y));
2315 	_XmHTMLDebug(5, ("layout.c, SetNone: updated box dimensions: %i, %i\n",
2316 		box->width, box->height));
2317 }
2318 
2319 /*****
2320 * Name: 		SetRule
2321 * Return Type: 	computes the offsets & position of a horizontal rule.
2322 * Description:
2323 * In:
2324 *	html:		XmHTMLWidget id;
2325 *	box:		current minipage;
2326 *	data:		element data.
2327 * Returns:
2328 *	nothing.
2329 *****/
2330 static void
SetRule(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2331 SetRule(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2332 {
2333 	int width = box->width;
2334 	int left = box->lmargin;
2335 	int y_offset;
2336 
2337 	_XmHTMLDebug(5, ("layout.c, SetRule: initial box position: %i, %i\n",
2338 		box->x, box->y));
2339 	_XmHTMLDebug(5, ("layout.c, SetRule: initial box dimensions: %i, %i\n",
2340 		box->width, box->height));
2341 
2342 	/* horizontal offset */
2343 	box->x = left + data->ident;
2344 
2345 	/* See if we have a width specification */
2346 	if(data->len != 0)
2347 	{
2348 		if(data->len < 0)	/* % spec */
2349 			width *= (float)(-1*data->len/100.);
2350 		else	/* pixel spec, cut if wider than available */
2351 			width = (data->len > width ? width : data->len);
2352 		/* alignment is only honored if there is a width spec */
2353 		switch(data->halign)
2354 		{
2355 			case XmHALIGN_RIGHT:
2356 				box->x = left + box->width - width;
2357 				break;
2358 			case XmHALIGN_CENTER:
2359 				box->x = left + (box->width - width - left)/2;
2360 			default:	/* shutup compiler */
2361 				break;
2362 		}
2363 	}
2364 	/* Save position and width */
2365 	data->x = box->x;
2366 	data->line  = line;
2367 	data->width = width;
2368 
2369 	/* Rules always reset to the x-position to the left margin */
2370 	box->x = left;
2371 
2372 	/*****
2373 	* Rules are always centered between lines
2374 	*
2375 	* ---> |--
2376 	* ---> | 	} line 1   ---| rule upper side  ---|
2377 	* ---> |--                |                     | box height
2378 	* ---> |--                |                     |
2379 	* ---> | 	} line 2   ---| rule lower side  ---|
2380 	* ---> |--
2381 	*
2382 	* Baseline of line 1 is given by box->y.
2383 	*****/
2384 
2385 	/*****
2386 	* Linefeeds in rules are divided accross the rule: half above and
2387 	* half below.
2388 	*****/
2389 	if(data->linefeed)
2390 		y_offset = data->linefeed/2;
2391 	else
2392 		y_offset = data->font->height/2;
2393 
2394 	data->y = box->y + y_offset;
2395 
2396 	/* take height of rule into account as well */
2397 	y_offset += data->height/2;
2398 
2399 	/* full height of the box */
2400 	box->height = 2*y_offset;
2401 
2402 	box->y += box->height;
2403 
2404 	/* a line above and one below */
2405 	line += 2;
2406 
2407 	_XmHTMLDebug(5, ("layout.c, SetRule: updated box position: %i, %i\n",
2408 		box->x, box->y));
2409 	_XmHTMLDebug(5, ("layout.c, SetRule: updated box dimensions: %i, %i\n",
2410 		box->width, box->height));
2411 }
2412 
2413 /*****
2414 * Name: 		SetBullet
2415 * Return Type: 	void
2416 * Description: 	computes the position & offsets for a list leader (can be a
2417 *				bullet or number).
2418 * In:
2419 *	html:		XmHTMLWidget id;
2420 *	box:		current minipage;
2421 *	data:		element data.
2422 * Returns:
2423 *	nothing. Upon return the current element has it's position set.
2424 * Note:
2425 *	Bullets always carry indentation within them. This indentation is the
2426 *	*total* indentation from the left margin and therefore the x-position
2427 *	is computed using the left margin, and not the left position. As we
2428 *	want all bullets to be right aligned, the x-position of a bullet is
2429 *	computed as the left margin *plus* any indentation. When being rendered,
2430 *	the real position is the computed position *minus* the width of the bullet,
2431 *	making them right-aligned.
2432 *****/
2433 static void
SetBullet(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2434 SetBullet(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2435 {
2436 	_XmHTMLDebug(5, ("layout.c, SetBullet: initial box position: %i, %i\n",
2437 		box->x, box->y));
2438 	_XmHTMLDebug(5, ("layout.c, SetBullet: initial box dimensions: %i, %i\n",
2439 		box->width, box->height));
2440 
2441 	/* save vertical position */
2442 	data->y = box->y + data->font->m_ascent;
2443 
2444 	/* linefeed if not at left margin */
2445 	if(box->x != box->lmargin)
2446 		line++;
2447 	box->y += data->linefeed;
2448 	box->x = box->lmargin + data->ident;
2449 
2450 	/* we have a left offset */
2451 	box->left = box->x;
2452 
2453 	data->x = box->x;
2454 	data->line = line;
2455 
2456 	_XmHTMLDebug(5, ("layout.c, SetBullet: updated box position: %i, %i\n",
2457 		box->x, box->y));
2458 	_XmHTMLDebug(5, ("layout.c, SetBullet: updated box dimensions: %i, %i\n",
2459 		box->width, box->height));
2460 }
2461 
2462 /*****
2463 * Name:			SetBreak
2464 * Return Type: 	void
2465 * Description:  inserts a linebreak and increments the vertical offset
2466 * In:
2467 *	html:		XmHTMLWidget id;
2468 *	box:		current minipage;
2469 *	data:		element data.
2470 * Returns:
2471 *	nothing.
2472 *****/
2473 static void
SetBreak(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2474 SetBreak(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2475 {
2476 	int linefeed = data->linefeed;
2477 	int dh;
2478 
2479 	_XmHTMLDebug(5, ("layout.c, SetBreak: initial box position: %i, %i\n",
2480 		box->x, box->y));
2481 	_XmHTMLDebug(5, ("layout.c, SetBreak: initial box dimensions: %i, %i\n",
2482 		box->width, box->height));
2483 
2484 	/* position of this break */
2485 	data->y = box->y + data->font->m_ascent;
2486 	data->x = box->x;
2487 
2488 	/* check if this is a real linebreak or just a margin reset */
2489 	if(linefeed)
2490 	{
2491 		/* if we already had a linefeed, we can substract one */
2492 		if(had_break && baseline_obj)
2493 		{
2494 			linefeed -= baseline_obj->font->lineheight;
2495 			had_break = False;
2496 		}
2497 		/* no negative linefeeds!! */
2498 		if(linefeed > 0)
2499 		{
2500 			line++;
2501 			box->y += data->linefeed;
2502 			/* update box height */
2503 			box->height = linefeed;
2504 		}
2505 	}
2506 
2507 	/* reset margin */
2508 	box->x = box->lmargin + data->ident;
2509 	box->left = box->x;
2510 
2511 	data->line = line;
2512 
2513 	/* height of this linefeed */
2514 	if((dh = box->y - data->y) < 0)	/* happens if we don't have a linefeed */
2515 		data->height = 0;
2516 	else
2517 		data->height = dh;
2518 
2519 	_XmHTMLDebug(5, ("layout.c, SetBreak: updated box position: %i, %i\n",
2520 		box->x, box->y));
2521 	_XmHTMLDebug(5, ("layout.c, SetBreak: updated box dimensions: %i, %i\n",
2522 		box->width, box->height));
2523 }
2524 
2525 /*****
2526 * Name:
2527 * Return Type:
2528 * Description:
2529 * In:
2530 *
2531 * Returns:
2532 *
2533 *****/
2534 static XmHTMLObjectTableElement
SetTable(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement data)2535 SetTable(XmHTMLWidget html, PositionBox *box, XmHTMLObjectTableElement data)
2536 {
2537 	XmHTMLTable *table;
2538 	TableRow *row = NULL;
2539 	TableCell *cell = NULL;
2540 	PositionBox **boxes = NULL;	/* 2D position matrix		*/
2541 	int *rows = NULL;			/* maximum row heights		*/
2542 	int *min_cols = NULL;		/* minimum width column dimensions	*/
2543 	int *max_cols = NULL;		/* maximum width column dimensions	*/
2544 	int i, j, k, idx;
2545 	int max_twidth = 0, min_twidth = 0, max_theight = 0;
2546 	int twidth, ncols, nrows, hpad, vpad, hspace, vspace, bwidth;
2547 	int full_twidth, usable_twidth;
2548 	Cardinal x_pos, y_pos, x_start;
2549 	int save_line, max_line;
2550 	Boolean needs_height_adjustment = False;
2551 	static int depth;
2552 
2553 	/* pick up table data */
2554 	table = data->table;
2555 
2556 	/*****
2557 	* The first table in a stack of tables contains all data for all
2558 	* table childs it contains. The first table child is the master
2559 	* table itself. So when a table doesn't have a child table it *is*
2560 	* a child table itself and thus we should add the left offset
2561 	* to the initial horizontal position.
2562 	*****/
2563 	if(table->childs)
2564 	{
2565 		table = &(table->childs[0]);
2566 		x_start = box->x;
2567 	}
2568 	else
2569 		x_start = box->left;
2570 
2571 	/* table position */
2572 	data->x = x_start;
2573 	data->y = box->y;
2574 
2575 	/* reserve room for outer table border */
2576 	x_start += table->props->border;
2577 
2578 	_XmHTMLDebug(17, ("layout.c: ------ Table Layout Starts -----\n"));
2579 	_XmHTMLDebug(17, ("layout.c: table starts near line %i and ends at line "
2580 		"%i in input).\n", table->start->object->line,
2581 		table->end->object->line));
2582 	_XmHTMLDebug(17, ("layout.c: initial box position: %i, %i\n",
2583 		box->x, box->y));
2584 	_XmHTMLDebug(17, ("layout.c: initial box dimensions: %i, %i\n",
2585 		box->width, box->height));
2586 	_XmHTMLDebug(17, ("layout.c: table has %i rows and %i columns.\n",
2587 		table->nrows, table->ncols));
2588 	_XmHTMLDebug(17, ("layout.c: table depth: %i\n", depth + 1));
2589 	depth++;
2590 
2591 	/* compute correct indentation */
2592 	if(box->x > box->lmargin + data->ident)
2593 		i = box->x;
2594 	else
2595 		i = box->lmargin + data->ident;
2596 
2597 	/*****
2598 	* Set maximum table width.
2599 	* Note that we ignore any percentage width if the width of the given
2600 	* box is negative: this can happen when we are performing nested
2601 	* layout calculations for tables (or cells) which don't have a set width.
2602 	* Also note that this can only happen when *precomputing* the table
2603 	* layout of the parent table (or cell), and that's why we don't
2604 	* check the validity of the box width in the second part of this
2605 	* conditional: if the given width is negative, it will always be
2606 	* -1 (indicating an unknown table width).
2607 	*****/
2608 	if(table->width > 0 || (table->width < 0 && box->width > 0))
2609 	{
2610 		/* width < 0 : percentage width */
2611 		if(table->width < 0)
2612 			twidth = (int)((-table->width * box->width)/100.);
2613 		else
2614 			twidth = table->width;
2615 	}
2616 	else
2617 	{
2618 		/* if this is a child table, assume a 100% width */
2619 		if(!table->childs)
2620 			twidth = box->width;
2621 		else
2622 		{
2623 			/* parent table, use whatever we can get our hands on */
2624 			if((twidth = box->width - i) <= 0)
2625 				twidth = -1;
2626 		}
2627 	}
2628 	_XmHTMLDebug(17, ("layout.c: initial table width: %i, ident: %i\n",
2629 		twidth, data->ident));
2630 
2631 	/* save current line number count */
2632 	save_line = line;
2633 
2634 	/* total no of rows and columns this table is made up of */
2635 	nrows = table->nrows;
2636 	ncols = table->ncols;
2637 
2638 	if(ncols == 0 || nrows == 0)
2639 	{
2640 		_XmHTMLWarning(__WFUNC__(html, "SetTable"), XMHTML_MSG_80);
2641 		return(table->end);
2642 	}
2643 	/*****
2644 	* Sanity Check: check all cells in search of a rowspan attribute. If
2645 	* we detect a rowspan in the *last* cell of a row, we must add a bogus
2646 	* cell to this row. If we don't do this, any cells falling in this row
2647 	* will be skipped, causing text to disappear (at the least, in the worst
2648 	* case it will cause a crash 'cause any in the skipped text anchors
2649 	* are never detected).
2650 	*****/
2651 	for(i = 0; i < table->nrows; i++)
2652 	{
2653 		row = &(table->rows[i]);
2654 
2655 		for(j = 0; j < row->ncells; j++)
2656 		{
2657 			if(row->cells[j].rowspan > 1 && (j+1) == ncols)
2658 				ncols++;
2659 		}
2660 	}
2661 
2662 	/* allocate a box for each row in this table */
2663 	boxes = (PositionBox**)calloc(nrows, sizeof(PositionBox*));
2664 
2665 		/* allocate a range of boxes spanning all columns */
2666 	for(i = 0; i < nrows; i++)
2667 		boxes[i] = (PositionBox*)calloc(ncols, sizeof(PositionBox));
2668 
2669 	/*****
2670 	* Step One: check if we have cells spanning multiple rows or columns.
2671 	* We always assume the table is rectangular: each row has the same
2672 	* amount of columns. If a cell is really being used, it will have a
2673 	* positive box index. A box with a negative index value means that
2674 	* this is a bogus cell spanned by it's neighbouring cells (which can be
2675 	* in another row).
2676 	*****/
2677 	_XmHTMLDebug(17, ("Checking for spanned cells\n"));
2678 	for(i = 0; i < nrows; i++)
2679 	{
2680 		row = &(table->rows[i]);
2681 
2682 		for(j = 0, idx = 0; j < ncols && idx < row->ncells; j++)
2683 		{
2684 			/* can happen when a cell spans multiple rows */
2685 			if(boxes[i][j].idx == -1)
2686 				continue;
2687 
2688 			cell = &(row->cells[idx]);
2689 
2690 			/* adjust col & rowspan if not set or incorrect */
2691 			if(cell->colspan <= 0 || cell->colspan > ncols)
2692 				cell->colspan = ncols;
2693 			if(cell->rowspan <= 0 || cell->rowspan > nrows)
2694 				cell->rowspan = nrows;
2695 
2696 			boxes[i][j].idx = idx;
2697 			boxes[i][j].colspan = cell->colspan;
2698 			boxes[i][j].rowspan = cell->rowspan;
2699 
2700 			if(cell->colspan != 1)
2701 			{
2702 				_XmHTMLDebug(17, ("Cell(%i,%i,%i): horizontal span leader, "
2703 					"(%i columns)\n", depth, i, j, cell->colspan));
2704 				/* subsequent cells are spanned by this cell */
2705 				for(k = 1; (j + k) < ncols && k < cell->colspan; k++)
2706 				{
2707 					_XmHTMLDebug(17, ("Cell(%i,%i,%i): horizontal span\n",
2708 						depth, i, j+k));
2709 					boxes[i][j + k].idx = -1;
2710 					boxes[i][j + k].colspan = - 1;
2711 				}
2712 				/* update cell counter to last spanned cell */
2713 				j += (k-1);
2714 			}
2715 			if(cell->rowspan != 1)
2716 			{
2717 				_XmHTMLDebug(17, ("Cell(%i,%i,%i): vertical span leader, "
2718 					"(%i rows)\n", depth, i, j, cell->rowspan));
2719 				/* subsequent rows are spanned by this cell */
2720 				for(k = 1; (i + k) < nrows && k < cell->rowspan; k++)
2721 				{
2722 					_XmHTMLDebug(17, ("Cell(%i,%i,%i): vertical span\n",
2723 						depth, i+k, j));
2724 					boxes[i + k][j].idx = -1;
2725 					boxes[i + k][j].rowspan = - 1;
2726 				}
2727 			}
2728 #ifdef DEBUG
2729 			if(cell->rowspan == 1 && cell->colspan == 1)
2730 			{
2731 				_XmHTMLDebug(17, ("Cell(%i,%i,%i): not spanned\n",
2732 						depth, i, j));
2733 			}
2734 #endif
2735 			idx++;
2736 		}
2737 		/* can happen for empty rows */
2738 		if(j != ncols)
2739 		{
2740 			for(; j < ncols; j++)
2741 			{
2742 				boxes[i][j].idx = -1;
2743 				_XmHTMLDebug(17, ("Cell(%i,%i,%i): unknown spanning\n",
2744 						depth, i, j));
2745 			}
2746 		}
2747 	}
2748 
2749 	/*****
2750 	* Step Two: compute minimum and maximum width of each cell.
2751 	* All precomputation is done without *any* margins, they will be
2752 	* added later.
2753 	*****/
2754 	_XmHTMLDebug(17, ("layout.c: ------ Table Precomputation Starts -----\n"));
2755 	_XmHTMLDebug(17, ("Cell(table depth, row, column)\n"));
2756 
2757 	_XmHTMLDebug(17, ("layout.c: Precomputing table dimensions:\n"));
2758 
2759 	for(i = 0; i < nrows; i++)
2760 	{
2761 		row = &(table->rows[i]);
2762 
2763 		/* compute desired cell dimensions */
2764 		for(j = 0; j < ncols; j++)
2765 		{
2766 			/* skip if this is a spanned cell */
2767 			if((idx = boxes[i][j].idx) != -1)
2768 				cell = &(row->cells[idx]);
2769 			else
2770 			{
2771 				_XmHTMLDebug(17, ("Cell(%i,%i,%i): spanned\n", depth, i, j));
2772 				continue;
2773 			}
2774 
2775 			/*****
2776 			* Offsets unused since these do not apply to the actual
2777 			* cell contents
2778 			*****/
2779 
2780 			/* do we have a width? */
2781 			if(cell->width)
2782 			{
2783 				/* is it relative to table width? */
2784 				if(cell->width < 0)
2785 				{
2786 					/* yes it is, do we have a table width? */
2787 					if(twidth != -1)
2788 						boxes[i][j].width = (-cell->width * twidth)/100.;
2789 					else
2790 						boxes[i][j].width = -1;
2791 				}
2792 				else /* absolute cell width */
2793 					boxes[i][j].width = cell->width;
2794 
2795 				_XmHTMLDebug(17, ("layout.c: Cell(%i,%i,%i) has initial width "
2796 					"%i\n", depth, i, j, boxes[i][j].width));
2797 			}
2798 			else
2799 				boxes[i][j].width = -1;
2800 
2801 			boxes[i][j].lmargin   = 0;
2802 			boxes[i][j].rmargin   = boxes[i][j].width;
2803 			boxes[i][j].min_width = boxes[i][j].width;
2804 
2805 			/*****
2806 			* Do we have a cell height? If so, it must be an absolute
2807 			* number. Can't do anything with relative heights.
2808 			*****/
2809 			boxes[i][j].height = cell->height > 0 ? cell->height : -1;
2810 
2811 			/*****
2812 			* Precompute the required dimensions for this cell.
2813 			* Upon return, the PositionBox will have updated values for
2814 			* width, min_width and height.
2815 			*****/
2816 			PreComputeTableLayout(html, &boxes[i][j], cell->start,
2817 				cell->end);
2818 
2819 			_XmHTMLDebug(17, ("Cell(%i,%i,%i): width = %i, min_width = %i, "
2820 				"height = %i %s\n", depth, i, j, boxes[i][j].width,
2821 				boxes[i][j].min_width, boxes[i][j].height,
2822 				cell->props->nowrap ? "(nowrap enabled)" : ""));
2823 		}
2824 	}
2825 
2826 	/*****
2827 	* Step Three: compute minimum and maximum row widths.
2828 	* The table layout is contained in the PositionBox matrix.
2829 	*****/
2830 
2831 	/* allocate room for minimum row dimensions */
2832 	rows = (int*)malloc(nrows * sizeof(int));
2833 
2834 	/* allocate room to store min & max column dimensions */
2835 	min_cols = (int*)malloc(ncols * sizeof(int));
2836 	max_cols = (int*)malloc(ncols * sizeof(int));
2837 
2838 	/* initialize to unknown sizes */
2839 	rows = (int*)memset(rows, -1, nrows * sizeof(int));
2840 	min_cols = (int*)memset(min_cols, -1, ncols * sizeof(int));
2841 	max_cols = (int*)memset(max_cols, -1, ncols * sizeof(int));
2842 
2843 	/*****
2844 	* Compute raw (content only, no margins, padding or border taken
2845 	* into account) minimum & maximum column widths and row heights
2846 	*****/
2847 	for(i = 0; i < nrows; i++)
2848 	{
2849 		int row_max_height = 0;
2850 
2851 		/* get current row */
2852 		row = &(table->rows[i]);
2853 
2854 		/* walk all cells in this row */
2855 		for(j = 0; j < ncols; j++)
2856 		{
2857 			/* skip if this is a spanned cell */
2858 			if((idx = boxes[i][j].idx) != -1)
2859 				cell = &(row->cells[idx]);
2860 			else
2861 				continue;
2862 
2863 			/*****
2864 			* If this cell spans multiple columns, check if the sum of the
2865 			* next columns can accomodate the minimum width required for
2866 			* displaying the contents of this cell.
2867 			* If it can, skip this cell (ensures all columns do get their
2868 			* minimum width, looks a lot nicer).
2869 			*****/
2870 			if(cell->colspan != 1)
2871 			{
2872 				int z, cwmin = 0;
2873 				for(z = j; z < ncols && z < (j + cell->colspan); z++)
2874 				{
2875 					cwmin += min_cols[z];
2876 				}
2877 
2878 				/*****
2879 				* Get maximum row height before checking, we don't want
2880 				* this row to have a zero height if it spans the entire
2881 				* table!
2882 				*****/
2883 				if(row_max_height < boxes[i][j].height)
2884 					row_max_height = boxes[i][j].height;
2885 
2886 				if(boxes[i][j].min_width < cwmin)
2887 					continue;
2888 			}
2889 
2890 			/*****
2891 			* Get largest minimum column width.
2892 			* If nowrap is in effect for this cell, compare against
2893 			* maximum cell width.
2894 			*****/
2895 			if(cell->props->nowrap)
2896 			{
2897 				if(min_cols[j] < boxes[i][j].width)
2898 					min_cols[j] = boxes[i][j].width;
2899 			}
2900 			else
2901 			{
2902 				if(min_cols[j] < boxes[i][j].min_width)
2903 					min_cols[j] = boxes[i][j].min_width;
2904 			}
2905 
2906 			/* Get smallest maximum column width */
2907 			if(max_cols[j] < boxes[i][j].width)
2908 				max_cols[j] = boxes[i][j].width;
2909 
2910 			/* get maximum row height */
2911 			if(row_max_height < boxes[i][j].height)
2912 				row_max_height = boxes[i][j].height;
2913 		}
2914 		/* store height for this row */
2915 		rows[i] = row_max_height;
2916 
2917 		/* and update table height */
2918 		max_theight += row_max_height;
2919 	}
2920 
2921 	/*****
2922 	* Compute room available for cell width adjustment, which is given by
2923 	* the set table width (if any) minus all spacing.
2924 	*
2925 	* Spacing: external cell spacing for left, right, top and bottom (border to
2926 	*         border). This represents the cellspacing table attribute.
2927 	*
2928 	* Vertical Spacing Rules
2929 	*  ---------------------
2930 	*	First row:
2931 	*	- full top spacing
2932 	*   - half bottom spacing;
2933 	*	Last row:
2934 	*	- half top spacing;
2935 	*	- full bottom spacing;
2936 	*	Other rows:
2937 	*	- half top spacing;
2938 	*	- half bottom spacing;
2939 	*
2940 	* Horizontal Spacing Rules
2941 	* ------------------------
2942 	*	First column:
2943 	*	- full left spacing;
2944 	*	- half right spacing;
2945 	*	Last column:
2946 	*	- half left spacing;
2947 	*	- full right spacing;
2948 	*	Other columns:
2949 	*	- half left spacing;
2950 	*	- half right spacing;
2951 	*
2952 	* Padding: internal cell spacing for left, right, top and bottom (text to
2953 	*          border). This represents the cellpadding table attribute.
2954 	* Rule: Add twice for each cell (left & right, top & bottom).
2955 	*
2956 	* Border: outer table and inner cell border width.
2957 	* Use of innercell borders is influenced by the presence of cellspacing:
2958 	* if cellspacing is present, inner cell borders aren't used since they
2959 	* will be overlapped by the cellspacing.
2960 	*
2961 	* Vertical Border Rules
2962 	* ---------------------
2963 	*	First row:
2964 	*	- full top borderwidth;
2965 	*	- single bottom borderwidth;
2966 	*	Last Row:
2967 	*	- single top borderwidth;
2968 	*	- full bottom borderwidth;
2969 	*	Other Rows, no vertical cellspacing:
2970 	*	- single top borderwidth;
2971 	*	- single bottom borderwidth;
2972 	*	Otherwise:
2973 	*	- zero top borderwidth;
2974 	*	- zero bottom borderwidth;
2975 	*
2976 	* Horizontal Border Rules
2977 	* -----------------------
2978 	*	First Column:
2979 	*	- full left borderwidth;
2980 	*	- single right borderwidth;
2981 	*	Last Column:
2982 	*	- single left borderwidth;
2983 	*	- full right borderwidth;
2984 	*	Other Columns, no horizontal cellspacing:
2985 	*	- single left borderwidth;
2986 	*	- single right borderwidth;
2987 	*	Otherwise:
2988 	*	- zero left borderwidth;
2989 	*	- zero right borderwidth;
2990 	*****/
2991 	hspace = table->hmargin;
2992 	vspace = table->vmargin;
2993 	hpad   = table->hpadding;
2994 	vpad   = table->vpadding;
2995 	max_twidth = 0;
2996 	min_twidth = 0;
2997 
2998 	/* inner cell spacing */
2999 	bwidth = table->props->border > 0 ? 1 : 0;
3000 
3001 	/* maximum & minimum table width, no margins */
3002 	for(i = 0; i < ncols; i++)
3003 	{
3004 		max_twidth += max_cols[i];
3005 		min_twidth += min_cols[i];
3006 	}
3007 
3008 	/* almost impossible but check anyway */
3009 	if(max_twidth < 1)
3010 	{
3011 		_XmHTMLWarning(__WFUNC__(html, "SetTable"),
3012 			"Overall maximum table width equal to or less than zero!\n");
3013 		max_twidth = 1;
3014 	}
3015 	if(min_twidth < 1)
3016 	{
3017 		_XmHTMLWarning(__WFUNC__(html, "SetTable"),
3018 			"Overall minimum table width equal to or less than zero!\n");
3019 		min_twidth = 1;
3020 	}
3021 
3022 	/* add inner cell border */
3023 	hspace += bwidth;
3024 
3025 	/* Add space taken up by all spacing and inner cell borders */
3026 	i = (ncols * (2*hpad + hspace)) + hspace;
3027 
3028 	/*****
3029 	* left & right *table* borders use full border width (and substracts
3030 	* one for the left & right most cells which was added above)
3031 	*****/
3032 	i += 2*(table->props->border - bwidth);
3033 
3034 	/*****
3035 	* Protect against underflow: set table width too small to include
3036 	* all requested spacing
3037 	*****/
3038 	if((usable_twidth = twidth - i) < min_twidth)
3039 		usable_twidth = min_twidth;		/* use minimum table width */
3040 
3041 #ifdef DEBUG
3042 	_XmHTMLDebug(17, ("layout.c: Computed table dimensions:\n"));
3043 	_XmHTMLDebug(17, ("layout.c: twidth          = %i\n", twidth));
3044 	_XmHTMLDebug(17, ("layout.c: usable twidth   = %i\n", usable_twidth));
3045 	_XmHTMLDebug(17, ("layout.c: max_twidth      = %i\n", max_twidth));
3046 	_XmHTMLDebug(17, ("layout.c: min_twidth      = %i\n", min_twidth));
3047 	_XmHTMLDebug(17, ("layout.c: max_theight     = %i\n", max_theight));
3048 	_XmHTMLDebug(17, ("layout.c: Column widths:\n"));
3049 
3050 	for(i = 0; i < ncols; i++)
3051 	{
3052 		_XmHTMLDebug(17, ("layout.c: column %i, min_width = %i, max_width = "
3053 			"%i\n", i, min_cols[i], max_cols[i]));
3054 	}
3055 	_XmHTMLDebug(17, ("layout.c: Row heights:\n"));
3056 	for(i = 0; i < nrows; i++)
3057 	{
3058 		_XmHTMLDebug(17, ("layout.c: row %i, height = %i\n", i, rows[i]));
3059 	}
3060 #endif
3061 
3062 	/*****
3063 	* Step Four: compute column widths.
3064 	*****/
3065 	/*****
3066 	* case 1: minimum width equal or wider than total width
3067 	* For nested tables, twidth can be -1 if the table with wasn't set
3068 	* to an absolute number. In this case set all columns to their minimum
3069 	* width.
3070 	*****/
3071 	if(twidth == -1 || min_twidth >= usable_twidth)
3072 	{
3073 #ifdef DEBUG
3074 		if(twidth == -1)
3075 		{
3076 			_XmHTMLDebug(17, ("layout.c: twidth unknown, using minimum "
3077 				"column widths.\n"));
3078 			twidth = min_twidth;
3079 		}
3080 		else
3081 			_XmHTMLDebug(17, ("layout.c: min_twidth > usable_twidth\n"));
3082 #endif
3083 		if(twidth == -1)
3084 			twidth = min_twidth;
3085 
3086 		/* assign each column it's minimum width */
3087 		for(i = 0; i < ncols; i++)
3088 			max_cols[i] = min_cols[i];
3089 
3090 		/* maximum table width */
3091 		usable_twidth = max_twidth = min_twidth;
3092 
3093 		/* needs a re-evaluation of row heights */
3094 		needs_height_adjustment = True;
3095 	}
3096 
3097 	/* case 2: maximum width less than total width */
3098 	else if(max_twidth < usable_twidth)
3099 	{
3100 		_XmHTMLDebug(17, ("layout.c: max_twidth < usable_twidth\n"));
3101 
3102 		/*****
3103 		* When a table has an absolute width (table->width nonzero), we
3104 		* stretch all columns so the available width is used up
3105 		* almost entirely (roundoff errors).
3106 		*****/
3107 		if(table->width)
3108 		{
3109 			min_twidth = 0;
3110 			for(i = 0; i < ncols; i++)
3111 			{
3112 				/* compute width percentage used by this column */
3113 				float pwidth = (float)max_cols[i]/(float)max_twidth;
3114 				max_cols[i] = (int)(pwidth*usable_twidth);
3115 				min_twidth += max_cols[i];
3116 			}
3117 			_XmHTMLDebug(17, ("layout.c: %i pixels short\n",
3118 				usable_twidth - min_twidth));
3119 
3120 			/* total width has been used */
3121 			max_twidth = min_twidth = usable_twidth;
3122 		}
3123 	}
3124 	/* case 3: max width exceeds available width while min width fits.*/
3125 	else
3126 	{
3127 		int nloop = 0;
3128 
3129 		/*****
3130 		* Loop this until the table fits the available width or we
3131 		* exceed the allowed no of iterations.
3132 		*****/
3133 		while(max_twidth > usable_twidth && nloop < XmHTML_MAX_TABLE_ITERATIONS)
3134 		{
3135 			/* Difference between available space and minimum table width */
3136 			float w_diff = (float)(usable_twidth - min_twidth);
3137 
3138 			/* Difference between maximum and minimum table width */
3139 			float m_diff = (float)(max_twidth - min_twidth);
3140 
3141 			/* prevent divide by zero */
3142 			if(m_diff == 0.0)
3143 				m_diff = 1.0;
3144 
3145 			_XmHTMLDebug(17, ("layout.c: min_twidth < usable_twidth && "
3146 				"max_twidth > usable_twidth\n"));
3147 			_XmHTMLDebug(17, ("layout.c: min_twidth = %i, max_twidth = %i, "
3148 				"usable_twidth = %i\n", min_twidth, max_twidth, usable_twidth));
3149 			_XmHTMLDebug(17, ("layout.c: w_diff = %.3f, m_diff = %.3f\n",
3150 				w_diff, m_diff));
3151 
3152 			/*****
3153 			* For each column, get the difference between minimum and maximum
3154 			* column width and scale using the above differences.
3155 			*****/
3156 			max_twidth = 0;
3157 			for(i = 0; i < ncols; i++)
3158 			{
3159 				float c_diff = max_cols[i] - min_cols[i];
3160 				max_cols[i]  = min_cols[i] + (int)(c_diff * (w_diff/m_diff));
3161 
3162 				/* update maximum width: add spacing */
3163 				max_twidth += max_cols[i];
3164 
3165 				_XmHTMLDebug(17, ("layout.c: Column %i, c_diff = %.3f, "
3166 					"width = %i\n", i, c_diff, max_cols[i]));
3167 			}
3168 			nloop++;
3169 		}
3170 		if(nloop == XmHTML_MAX_TABLE_ITERATIONS)
3171 		{
3172 			_XmHTMLWarning(__WFUNC__(NULL, "SetTable"),
3173 				XMHTML_MSG_79, "Table Layout", XmHTML_MAX_TABLE_ITERATIONS,
3174 				table->start->object->line);
3175 		}
3176 		usable_twidth = max_twidth;
3177 
3178 		/* needs a re-evaluation of row heights */
3179 		needs_height_adjustment = True;
3180 	}
3181 
3182 	/*****
3183 	* Step Five: recompute row heights if required.
3184 	* For a number of cells, the width will be less than the maximum cell
3185 	* width and thus lines will be broken. If we don't recompute the
3186 	* required box height, the table will overflow vertically.
3187 	*****/
3188 	if(needs_height_adjustment)
3189 	{
3190 		_XmHTMLDebug(17, ("layout.c: adjusting cell heights:\n"));
3191 
3192 		for(i = 0; i < nrows; i++)
3193 		{
3194 			int row_max_height = 0;
3195 
3196 			row = &(table->rows[i]);
3197 			for(j = 0; j < ncols; j++)
3198 			{
3199 				int row_max_width  = 0;
3200 
3201 				/* skip if this is a spanned cell */
3202 				if((idx = boxes[i][j].idx) != -1)
3203 					cell = &(row->cells[idx]);
3204 				else
3205 				{
3206 					_XmHTMLDebug(17,("Cell(%i,%i,%i): spanned\n",
3207 						depth, i, j));
3208 					continue;
3209 				}
3210 
3211 				/* box to wide, will be broken */
3212 				if(boxes[i][j].width > max_cols[j] ||
3213 					boxes[i][j].height == -1 ||
3214 					boxes[i][j].width  == -1)
3215 				{
3216 					/* offsets unused */
3217 					boxes[i][j].x       = 0;
3218 					boxes[i][j].y       = 0;
3219 					boxes[i][j].left    = 0;
3220 					boxes[i][j].right   = 0;
3221 
3222 					/* set margins */
3223 					boxes[i][j].lmargin = 0;
3224 
3225 					/* set right margin */
3226 					if(cell->colspan == 1)
3227 						boxes[i][j].rmargin = max_cols[j];
3228 					else
3229 					{
3230 						/* spans multiple columns, add up column widths */
3231 						int k;
3232 						boxes[i][j].rmargin = 0;
3233 						for(k = j; k < j + cell->colspan && k < ncols; k++)
3234 							boxes[i][j].rmargin += max_cols[k];
3235 					}
3236 					boxes[i][j].width = boxes[i][j].rmargin -
3237 											boxes[i][j].lmargin;
3238 					boxes[i][j].height= -1;
3239 
3240 					PreComputeTableLayout(html, &boxes[i][j], cell->start,
3241 						cell->end);
3242 
3243 					_XmHTMLDebug(17,("Cell(%i,%i,%i): "
3244 						"width = %i, height = %i\n", depth, i, j,
3245 						boxes[i][j].width, boxes[i][j].height));
3246 				}
3247 				else
3248 					_XmHTMLDebug(17, ("Cell(%i,%i,%i): no change\n",
3249 						depth, i, j));
3250 
3251 				/* update maximum row width */
3252 				row_max_width += boxes[i][j].width;
3253 
3254 				/* update maximum row height, taking spacing into account */
3255 				if(cell->rowspan == 1 && row_max_height < boxes[i][j].height)
3256 					row_max_height = boxes[i][j].height;
3257 
3258 				/*****
3259 				* Update table width if we're exceeding it, which should
3260 				* not be really happening as the cell width will only
3261 				* decrease: each cell already has it's minimum width.
3262 				*****/
3263 				if(max_twidth < row_max_width)
3264 				{
3265 					_XmHTMLDebug(17, ("layout.c: updating maximum table "
3266 						"width from %i to %i\n", max_twidth, row_max_width));
3267 					max_twidth = row_max_width;
3268 				}
3269 			}
3270 			rows[i] = row_max_height;
3271 		}
3272 #ifdef DEBUG
3273 		for(i = 0; i < nrows; i++)
3274 		{
3275 			_XmHTMLDebug(17, ("layout.c: row %i, height = %i\n", i, rows[i]));
3276 		}
3277 #endif
3278 	}
3279 
3280 	/*****
3281 	* Step Six: adjust row heights to account for rowspan attributes.
3282 	*
3283 	* Each (filled) cell has it's dimensions set. We now need to adjust
3284 	* the row heights to properly account for any rowspan attributes set.
3285 	* The way we do this is as follows: for each row, check if it contains
3286 	* a cell with the rowspan attribute set and get the one which spans the
3287 	* most rows. When found, compute the total height of all spanned rows
3288 	* and then compute the difference between this height and the height of
3289 	* the spanning cell. If this difference is negative, we can keep the
3290 	* current row heights. If it's positive however, we distribute this
3291 	* difference evenly accross the height of all spanned rows.
3292 	*****/
3293 	if(needs_height_adjustment)
3294 	{
3295 		_XmHTMLDebug(17, ("layout.c: rowspan in effect, re-adjusting row "
3296 			"heights :\n"));
3297 
3298 		for(i = 0; i < nrows; i++)
3299 		{
3300 			int max_span = 1;
3301 			int span_cell = -1;
3302 
3303 			row = &(table->rows[i]);
3304 			for(j = 0; j < ncols; j++)
3305 			{
3306 				/* skip if this is a spanned cell */
3307 				if((idx = boxes[i][j].idx) != -1)
3308 					cell = &(row->cells[idx]);
3309 				else
3310 					continue;
3311 				if(cell->rowspan > max_span)
3312 				{
3313 					max_span = cell->rowspan;
3314 					span_cell = j;
3315 				}
3316 			}
3317 			if(span_cell != -1 && span_cell < ncols)
3318 			{
3319 				/* height of spanning cell */
3320 				int max_h = boxes[i][span_cell].height;
3321 
3322 				/* compute height of all spanned rows */
3323 				int span_h = 0;
3324 
3325 				for(k = i; k < nrows && k < i + max_span; k++)
3326 					span_h += rows[k];
3327 
3328 				/* spanned height greater than occupied height, adjust rows */
3329 				if((max_h - span_h) > 0)
3330 				{
3331 					int extra = (max_h - span_h)/(float)max_span;
3332 					for(k = i; k < nrows && k < i + max_span; k++)
3333 						rows[k] += extra;
3334 				}
3335 			}
3336 		}
3337 #ifdef DEBUG
3338 		for(i = 0; i < nrows; i++)
3339 		{
3340 			_XmHTMLDebug(17, ("layout.c: row %i, height = %i\n", i, rows[i]));
3341 		}
3342 #endif
3343 	}
3344 
3345 	_XmHTMLDebug(17, ("layout.c: ------ Table Precomputation End -----\n"));
3346 
3347 	/*****
3348 	* Step Seven: assign box dimensions for each cell.
3349 	*****/
3350 	_XmHTMLDebug(17, ("layout.c: Final Cell Dimensions:\n"));
3351 
3352 	/*****
3353 	* Compute *full* table width. Full table width takes all spacing into
3354 	* account. Spacing is added as follows:
3355 	*****/
3356 	hspace = table->hmargin;
3357 	vspace = table->vmargin;
3358 	hpad   = table->hpadding;
3359 	vpad   = table->vpadding;
3360 
3361 	/* inner cell spacing */
3362 	bwidth = table->props->border > 0 ? 1 : 0;
3363 
3364 	/* Add space taken up by all spacing */
3365 	hspace += bwidth;
3366 	i = (ncols * (2*hpad + hspace)) + hspace;
3367 
3368 	/* Correct for outer border */
3369 	i += 2*(table->props->border - bwidth);
3370 	full_twidth = max_twidth + i;
3371 
3372 	/* reset */
3373 	hspace = table->hmargin;
3374 
3375 #ifdef DEBUG
3376 	_XmHTMLDebug(17, ("layout.c: Computed table dimensions:\n"));
3377 	_XmHTMLDebug(17, ("layout.c: twidth        = %i\n", twidth));
3378 	_XmHTMLDebug(17, ("layout.c: usable twidth = %i\n", usable_twidth));
3379 	_XmHTMLDebug(17, ("layout.c: max_twidth    = %i\n", max_twidth));
3380 	_XmHTMLDebug(17, ("layout.c: cell spacing  = %i\n", table->hmargin));
3381 	_XmHTMLDebug(17, ("layout.c: cell padding  = %i\n", table->hpadding));
3382 	_XmHTMLDebug(17, ("layout.c: inner border  = %i\n", bwidth));
3383 	_XmHTMLDebug(17, ("layout.c: outer border  = %i\n", table->props->border));
3384 	_XmHTMLDebug(17, ("layout.c: full_twidth   = %i\n", full_twidth));
3385 	_XmHTMLDebug(17, ("layout.c: Column widths:\n"));
3386 
3387 	for(i = 0; i < ncols; i++)
3388 	{
3389 		_XmHTMLDebug(17, ("layout.c: column %i, max_width = %i\n", i,
3390 			max_cols[i]));
3391 	}
3392 	_XmHTMLDebug(17, ("layout.c: Row heights:\n"));
3393 	for(i = 0; i < nrows; i++)
3394 	{
3395 		_XmHTMLDebug(17, ("layout.c: row %i, height = %i\n", i, rows[i]));
3396 	}
3397 #endif
3398 
3399 	/*****
3400 	* Check horizontal *table* alignment and compute initial
3401 	* horizontal offset.
3402 	*****/
3403 	_XmHTMLDebug(17, ("layout.c: checking horizontal alignment.\n"));
3404 	_XmHTMLDebug(17, ("layout.c: x_start = %i, max_twidth = %i, "
3405 		"twidth = %i\n", x_start, max_twidth, twidth));
3406 
3407 	switch((table->props->halign == XmHALIGN_NONE ?
3408 		HTML_ATTR(alignment) : table->props->halign))
3409 	{
3410 		case XmHALIGN_RIGHT:
3411 			x_start += (box->width - data->ident) > full_twidth ?
3412 						(box->width - data->ident) - full_twidth : 0;
3413 			break;
3414 		case XmHALIGN_CENTER:
3415 			x_start += (box->width - data->ident) > full_twidth ?
3416 						((box->width - data->ident) - full_twidth)/2 : 0;
3417 			break;
3418 		case XmHALIGN_LEFT:		/* computation is always left-oriented */
3419 		case XmHALIGN_JUSTIFY:	/* useless for tables */
3420 		default:
3421 			break;
3422 	}
3423 	_XmHTMLDebug(17, ("layout.c: computed x_start to be %i\n", x_start));
3424 
3425 	max_theight = 0;
3426 	max_twidth  = 0;
3427 
3428 	/*****
3429 	* Adjust upper left corner of table:
3430 	* vertical cellspacing moves the vertical offset downwards
3431 	*****/
3432 	x_pos = x_start + hspace;
3433 	y_pos = box->y + table->props->border + vspace;
3434 
3435 	for(i = 0; i < nrows; i++)
3436 	{
3437 		int tw = 0;
3438 		int hleft, hright, vtop, vbottom;
3439 
3440 		/*****
3441 		* Vertical Spacing Rules
3442 		*  ---------------------
3443 		*	First row:
3444 		*	- full top spacing
3445 		*   - half bottom spacing;
3446 		*	Last row:
3447 		*	- half top spacing;
3448 		*	- full bottom spacing;
3449 		*	Other rows:
3450 		*	- half top spacing;
3451 		*	- half bottom spacing;
3452 		*****/
3453 		if(i == 0)
3454 		{
3455 			/* vertical spacing and borderwidth already accounted for */
3456 			vtop = vpad + 1;
3457 			vbottom = 0.5*vspace + vpad + (vspace ? 0 : bwidth);
3458 		}
3459 		else if(i == nrows - 1)
3460 		{
3461 			vtop = 0.5*vspace + vpad + (vspace ? 0 : bwidth);
3462 			vbottom = vspace + vpad + table->props->border;
3463 		}
3464 		else
3465 		{
3466 			vtop = 0.5*vspace + vpad + (vspace ? 0 : bwidth);
3467 			vbottom = 0.5*vspace + vpad + (vspace ? 0 : bwidth);
3468 		}
3469 
3470 		/* pick up current row */
3471 		row = &(table->rows[i]);
3472 
3473 		/* top-left row positions */
3474 		x_pos = x_start;
3475 		row->owner->x = x_pos;
3476 		row->owner->y = y_pos;
3477 
3478 		for(j = 0; j < ncols; j++)
3479 		{
3480 			int cwidth = 0, cheight = 0;
3481 
3482 			/*****
3483 			* Horizontal Spacing Rules
3484 			* ------------------------
3485 			*	First column:
3486 			*	- full left spacing;
3487 			*	- half right spacing;
3488 			*	Last column:
3489 			*	- half left spacing;
3490 			*	- full right spacing;
3491 			*	Other columns:
3492 			*	- half left spacing;
3493 			*	- half right spacing;
3494 			*****/
3495 			if(j == 0)
3496 			{
3497 				/* borderwidth already accounted for (x_pos) */
3498 				hleft = hspace + hpad;
3499 				hright = 0.5*hspace + hpad + (hspace ? 0 : bwidth);
3500 			}
3501 			else if(j == ncols - 1)
3502 			{
3503 				hleft = 0.5*hspace + hpad + (hspace ? 0 : bwidth);
3504 
3505 				/* borderwidth already accounted for (x_pos) */
3506 				hright = hspace + hpad;
3507 			}
3508 			else
3509 			{
3510 				hleft = 0.5*hspace + hpad + (hspace ? 0 : bwidth);
3511 				hright = 0.5*hspace + hpad + (hspace ? 0 : bwidth);
3512 			}
3513 
3514 			/* pick up current cell if not a spanned cell */
3515 			if((idx = boxes[i][j].idx) != -1)
3516 				cell = &(row->cells[idx]);
3517 
3518 			/*****
3519 			* Initial start positions for this box. First cell in a row
3520 			* uses the set table border while all other cells use 1 or 0
3521 			*****/
3522 			boxes[i][j].x = x_pos;
3523 			boxes[i][j].y = y_pos;
3524 
3525 			/*****
3526 			* Set correct left & right margin
3527 			* Left & right margin take horizontal spacing into account.
3528 			*****/
3529 			boxes[i][j].lmargin = x_pos + hleft;
3530 			if(idx == -1 || cell->colspan == 1)
3531 			{
3532 				/* set right margin */
3533 				boxes[i][j].rmargin = boxes[i][j].lmargin + max_cols[j]+hright;
3534 
3535 				/* total cell width */
3536 				cwidth = max_cols[j] + hleft + hright;
3537 			}
3538 			else
3539 			{
3540 				/* spans multiple columns, add up column sizes */
3541 				int k;
3542 				boxes[i][j].rmargin = boxes[i][j].lmargin;
3543 				for(k = j; k < j + cell->colspan && k < ncols; k++)
3544 				{
3545 					/* left & right padding */
3546 					boxes[i][j].rmargin += (max_cols[k] + hleft + hright);
3547 
3548 					/* total cell width */
3549 					cwidth += max_cols[k] + hleft + hright;
3550 				}
3551 			}
3552 
3553 			/* set available cell width */
3554 			boxes[i][j].width = boxes[i][j].rmargin - boxes[i][j].lmargin;
3555 			boxes[i][j].left  = boxes[i][j].lmargin;
3556 			boxes[i][j].right = boxes[i][j].rmargin;
3557 
3558 			/* Set correct cell height. */
3559 			if(idx == -1 || cell->rowspan == 1)
3560 			{
3561 				boxes[i][j].height = rows[i] + vtop;
3562 				cheight = boxes[i][j].height + vtop ; /* + vbottom; */
3563 			}
3564 			else
3565 			{
3566 				/* spans multiple rows, add up the row heights it occupies */
3567 				int k;
3568 				boxes[i][j].height = 0;
3569 				for(k = i; k < i + cell->rowspan && k < nrows; k++)
3570 				{
3571 					boxes[i][j].height += rows[k] + vtop;
3572 					cheight += rows[k] + vtop + vbottom;
3573 				}
3574 
3575 			}
3576 
3577 			/*
3578 			 * FIXME: update for vertical positioning: get content height,
3579 			 * substract box height and shift down by difference (bottom
3580 			 * alignment) or by half the difference (middle alignment)
3581 			 * Should be done in next step!
3582 			 */
3583 			/* set vertical margins */
3584 			boxes[i][j].tmargin = vtop;
3585 			boxes[i][j].bmargin = boxes[i][j].height;
3586 
3587 			/*****
3588 			* Store bounding box dimensions for proper frame rendering, but
3589 			* *never* do this if the current cell is a spanned one. If we
3590 			* would do this the offsets would be horribly wrong...
3591 			*****/
3592 			if(idx != -1)
3593 			{
3594 				cell->owner->x = x_pos;
3595 				cell->owner->y = y_pos;
3596 				cell->owner->width  = cwidth;
3597 				cell->owner->height = cheight;
3598 			}
3599 
3600 			/*****
3601 			* advance x position to next column. Must include any padding &
3602 			* spacing.
3603 			*****/
3604 			x_pos += max_cols[j] + hleft + hright;
3605 			tw += max_cols[j] + hleft + hright;
3606 
3607 			_XmHTMLDebug(17, ("Cell(%i,%i,%i): x = %i, y = %i, w = %i, "
3608 				"h = %i, left = %i, right = %i %s\n\thleft = %i, hright = %i "
3609 				"htop = %i, hbottom = %i\n", depth, i, j,
3610 				boxes[i][j].x, boxes[i][j].y, boxes[i][j].width,
3611 				boxes[i][j].height, boxes[i][j].left, boxes[i][j].right,
3612 				idx == -1 ? "(spanned)" : "",
3613 				hleft, hright, vtop, vbottom));
3614 		}
3615 		/* update max_width if necessary */
3616 		if(x_pos > max_width)
3617 		{
3618 			/* adjust maximum document width */
3619 			max_width = x_pos;
3620 			_XmHTMLDebug(17, ("layout.c: adjusted max_width to %i\n",
3621 				max_width));
3622 		}
3623 		if(max_twidth < tw)
3624 			max_twidth = tw;
3625 
3626 		/* move to next row, row height already includes padding */
3627 		y_pos += rows[i] + vtop + vbottom;
3628 		max_theight += rows[i] + vtop + vbottom;
3629 
3630 		/* save row dimensions */
3631 		row->owner->width = x_pos - row->owner->x;
3632 		row->owner->height = y_pos - row->owner->y;
3633 	}
3634 
3635 	/* surrounding border */
3636 	max_twidth  += 2*table->props->border;
3637 	max_theight += 2*table->props->border;
3638 
3639 	/* Final table height */
3640 #ifdef DEBUG
3641 	if(max_theight != (y_pos - box->y))
3642 	{
3643 		_XmHTMLDebug(17, ("layout.c: Adjusted table height "
3644 			"from %i to %i\n", max_theight, y_pos - box->y));
3645 		max_theight = y_pos - box->y;
3646 	}
3647 	/* Final table height */
3648 	if(full_twidth != max_twidth)
3649 	{
3650 		_XmHTMLDebug(17, ("layout.c: Adjusted table width "
3651 			"from %i to %i\n", full_twidth, max_twidth));
3652 		full_twidth = max_twidth;
3653 	}
3654 #else
3655 	max_theight = y_pos - box->y;
3656 	full_twidth = max_twidth;
3657 #endif
3658 
3659 	/* restore line count */
3660 	line = save_line;
3661 
3662 	/* save start line for this table for both the data & owning object */
3663 	data->line = table->start->line = line;
3664 
3665 	/*****
3666 	* Step Eight: compute real text layout using the computed box dimensions.
3667 	*****/
3668 	for(i = 0; i < table->nrows; i++)
3669 	{
3670 		row = &(table->rows[i]);
3671 
3672 		/* restore line count for each row */
3673 		line = save_line;
3674 
3675 		/* set line for this row on both owner and start object */
3676 		row->owner->line = row->start->line = line;
3677 
3678 		max_line = 0;
3679 
3680 		/* layout all cells in this row */
3681 		for(j = 0; j < ncols; j++)
3682 		{
3683 			/* skip if this is a spanned cell */
3684 			if((idx = boxes[i][j].idx) != -1)
3685 				cell = &(row->cells[idx]);
3686 			else
3687 				continue;
3688 
3689 			/* set line number for owner as well! */
3690 			cell->owner->line = line = save_line;
3691 
3692 			_XmHTMLDebug(17, ("layout.c: Cell(%i,%i,%i): computing final "
3693 				"layout for box at (%i,%i) with size %ix%i\n", depth, i, j,
3694 				boxes[i][j].x, boxes[i][j].y,
3695 				boxes[i][j].width, boxes[i][j].height));
3696 
3697 			/* compute final layout for the current cell */
3698 			ComputeTableLayout(html, &boxes[i][j], cell->start, cell->end);
3699 
3700 			_XmHTMLDebug(17, ("layout.c: Cell(%i,%i,%i): done computing final "
3701 				"layout\n", depth, i, j));
3702 
3703 			/* adjust cell contents vertically if not aligned at cell top */
3704 			if(cell->props->valign != XmVALIGN_TOP)
3705 				CheckVerticalAlignment(html, &boxes[i][j], cell->start,
3706 					cell->end, cell->props->valign);
3707 
3708 			/*****
3709 			* Update line number from the last valid object: the end of
3710 			* a cell is a non-renderable object but is used to get and
3711 			* set the document position. Therefore, the line number of
3712 			* the cell end should be equal to the last renderable object.
3713 			*****/
3714 			if(cell->end && cell->end->prev &&
3715 					cell->end->prev->object_type != OBJ_NONE)
3716 				cell->end->line = cell->end->prev->line;
3717 
3718 			/* store maximum line count in this row */
3719 			/* fix 09/23/98-01, dw */
3720 			if(max_line < line - save_line)
3721 				max_line = line - save_line;
3722 		}
3723 		/* save dimensions on end object for this row */
3724 		if(row->end == row->start)
3725 		{
3726 			/* empty row! */
3727 			row->start->x = row->owner->x;
3728 			row->start->y = row->owner->y;
3729 			row->start->height = row->owner->height;
3730 			row->start->line   = row->owner->line;
3731 		}
3732 		else if(row->end)
3733 		{
3734 			row->end->x = row->start->x;
3735 			row->end->y = row->start->y + row->start->height;
3736 			row->end->height = 0;
3737 
3738 			/*****
3739 			* Get line number from the last valid object: the end of
3740 			* a row is a non-renderable object but is used to get and
3741 			* set the document position. Therefore, the line number of
3742 			* the row end should be equal to the last renderable object.
3743 			*****/
3744 			if(row->end->prev && row->end->prev->object_type != OBJ_NONE)
3745 				row->end->line = row->end->prev->line;
3746 			else
3747 				row->end->line = save_line + max_line;
3748 		}
3749 		/*****
3750 		* Row done, adjust linecount. Each row advances line count
3751 		* with at least 1 line!!!
3752 		*****/
3753 		save_line += max_line ? max_line : 1;
3754 	}
3755 
3756 #ifdef DEBUG
3757 	_XmHTMLDebug(17, ("layout.c: dumping final cell dimensions\n"));
3758 	k = 0;
3759 	for(i = 0; i < table->nrows; i++)
3760 	{
3761 		k += boxes[i][0].height;
3762 		for(j = 0; j < ncols; j++)
3763 		{
3764 			_XmHTMLDebug(17, ("layout.c: Cell(%i,%i,%i): x = %i, y = %i, "
3765 				"w = %i, h = %i\n", depth, i, j,
3766 				boxes[i][j].x, boxes[i][j].y,
3767 				boxes[i][j].width, boxes[i][j].height));
3768 		}
3769 	}
3770 	_XmHTMLDebug(17, ("layout.c: max_theight = %i, sum of rows = %i\n",
3771 		max_theight, k));
3772 	_XmHTMLDebug(17, ("layout.c: done dumping final cell dimensions\n"));
3773 #endif
3774 
3775 	/* all done, set correct linenumber */
3776 	line = save_line;
3777 
3778 	/* All done, free the allocated boxes */
3779 	for(i = 0; i < table->nrows; i++)
3780 	{
3781 		/* all cells in this row */
3782 		free(boxes[i]);
3783 	}
3784 	free(boxes);
3785 
3786 	/* free row & column size storage */
3787 	free(rows);
3788 	free(max_cols);
3789 	free(min_cols);
3790 
3791 	/* store return dimensions, box->x is not touched */
3792 	box->y += max_theight;
3793 	box->height = max_theight;
3794 
3795 	/* save dimensions on end object for this table */
3796 	if(table->end)
3797 	{
3798 		table->end->x = data->x;
3799 		table->end->y = data->y + max_theight;
3800 		table->end->height = max_theight;
3801 		table->end->line   = line;
3802 	}
3803 
3804 	/* only adjust box width if initial width wasn't preset */
3805 	if(box->width != -1 || box->width < full_twidth)
3806 	{
3807 		box->width = full_twidth;
3808 		box->min_width = full_twidth;
3809 	}
3810 
3811 	/*****
3812 	* update x position of owning object, it might have shifted due to
3813 	* horizontal alignment adjustment at table level.
3814 	*****/
3815 	data->x = x_start - table->props->border;
3816 
3817 	/* final (absolute) table dimensions */
3818 	data->height = max_theight;
3819 	data->width  = full_twidth;
3820 
3821 	/* adjust maximum document width */
3822 	if(box->x + full_twidth > max_width)
3823 	{
3824 		max_width = box->x + full_twidth;
3825 		_XmHTMLDebug(17, ("layout.c: adjusted max_width to %i\n", max_width));
3826 	}
3827 
3828 	_XmHTMLDebug(17, ("layout.c: table at depth %i finished, table width: %i, "
3829 		"table height: %i\n", depth, max_twidth, max_theight));
3830 	_XmHTMLDebug(17, ("layout.c: next box starts at: %i, %i\n",
3831 		box->x, box->y));
3832 
3833 	depth--;
3834 
3835 	_XmHTMLDebug(17, ("layout.c, SetTable end, dept = %i, return box:\n"
3836 		"x = %i, y = %i, width = %i, height = %i,\n"
3837 		"left = %i, right = %i, lmargin = %i, rmargin = %i\n",
3838 		depth, box->x, box->y, box->width, box->height, box->left, box->right,
3839 		box->lmargin, box->rmargin));
3840 
3841 	_XmHTMLDebug(17, ("layout.c: end of table started near line %i and ending "
3842 		"at line %i in input).\n", table->start->object->line,
3843 		table->end->object->line));
3844 	_XmHTMLDebug(17, ("layout.c: ------ Table Layout Ends -----\n"));
3845 
3846 	/* all done! */
3847 	return(table->end);
3848 }
3849 
3850 /*****
3851 * Name:
3852 * Return Type:
3853 * Description:
3854 * In:
3855 *
3856 * Returns:
3857 *
3858 *****/
3859 static void
PreComputeTableLayout(XmHTMLWidget html,PositionBox * parent,XmHTMLObjectTableElement obj_start,XmHTMLObjectTableElement obj_end)3860 PreComputeTableLayout(XmHTMLWidget html, PositionBox *parent,
3861 	XmHTMLObjectTableElement obj_start, XmHTMLObjectTableElement obj_end)
3862 {
3863 	XmHTMLObjectTableElement tmp, end;
3864 	int max_width_save;
3865 	PositionBox box, box_return;
3866 	int y_start = parent->y;
3867 
3868 	memcpy(&box, parent, sizeof(PositionBox));
3869 	memcpy(&box_return, parent, sizeof(PositionBox));
3870 
3871 	/*****
3872 	* Save current max_width, it's possible it will be changed in
3873 	* CellSetText (or any of the routines it calls) but if it does it's a bogus
3874 	* value (width is ignored when precomputing cell widths).
3875 	*****/
3876 	max_width_save = max_width;
3877 	had_break = False;
3878 	baseline_obj = NULL;
3879 	box.y = 0;
3880 	box.x = 0;
3881 
3882 	for(tmp = obj_start; tmp && tmp != obj_end; tmp = tmp->next)
3883 	{
3884 		switch(tmp->object_type)
3885 		{
3886 			case OBJ_TEXT:
3887 				/* collect all words */
3888 				for(end = tmp; end->next->object_type == OBJ_TEXT;
3889 					end = end->next);
3890 
3891 				/* go and do text layout */
3892 				SetText(html, &box, tmp, end->next, False, True);
3893 
3894 				/* back up one element */
3895 				tmp = end;
3896 				break;
3897 
3898 			case OBJ_PRE_TEXT:
3899 				/* collect all words */
3900 				for(end = tmp; end->next->object_type == OBJ_PRE_TEXT;
3901 					end = end->next);
3902 
3903 				/* go and do text layout */
3904 				SetText(html, &box, tmp, end->next, True, True);
3905 
3906 				/* back up one element */
3907 				tmp = end;
3908 				break;
3909 			case OBJ_BULLET:
3910 				SetBullet(html, &box, tmp);
3911 				break;
3912 			case OBJ_HRULE:
3913 				SetRule(html, &box, tmp);
3914 				break;
3915 			case OBJ_TABLE:
3916 				_XmHTMLDebug(17, ("Precompute: nested table, current "
3917 					"box dimensions:\nwidth = %i, min_width = %i, "
3918 					"height = %i %s\n", box.width, box.min_width, box.height));
3919 				SetBlock(html, &box, tmp);
3920 				tmp = SetTable(html, &box, tmp);
3921 				break;
3922 			case OBJ_TABLE_FRAME:
3923 				break;
3924 			case OBJ_APPLET:
3925 				SetApplet(html, &box, tmp);
3926 				SetBreak(html, &box, tmp);
3927 				break;
3928 			case OBJ_BLOCK:
3929 				SetBlock(html, &box, tmp);
3930 				SetBreak(html, &box, tmp);
3931 				break;
3932 			case OBJ_NONE:
3933 				SetBlock(html, &box, tmp);
3934 				break;
3935 			default:
3936 				_XmHTMLWarning(__WFUNC__(html, "PreComputeLayout"),
3937 					XMHTML_MSG_78);
3938 		}
3939 		/* store maximum box width */
3940 		if(box_return.width < box.width)
3941 			box_return.width = box.width;
3942 		/* store minimum box width (ignore empty boxes) */
3943 		if(box.min_width > 0 && box_return.min_width < box.min_width)
3944 			box_return.min_width = box.min_width;
3945 		/*****
3946 		* Reset box width to default value for each object being
3947 		* precomputed: text layout stores the maximum width, which can
3948 		* give evil results for nested tables.
3949 		*****/
3950 		box.width = parent->width;
3951 		box.min_width = parent->min_width;
3952 	}
3953 	/* Done precomputing, update return values */
3954 
3955 	/* maximum box width */
3956 	if(parent->width == -1)
3957 	{
3958 		parent->width = box_return.width;
3959 		parent->min_width = box_return.min_width;
3960 	}
3961 	else
3962 	{
3963 		/* initial width provided. Only update if it's smaller */
3964 		if(parent->width < box_return.width)
3965 			parent->width = box_return.width;
3966 		if(parent->min_width < box_return.min_width)
3967 			parent->min_width = box_return.min_width;
3968 	}
3969 
3970 	/*****
3971 	* Update box height if the computed height is larger than the set
3972 	* height (can only happen for cells with the height attribute set).
3973 	*****/
3974 	if(box_return.height != -1)
3975 	{
3976 		/* don't ask me how this is possible, but it *does* happen */
3977 		if(box_return.height < 0)
3978 			parent->height = box.y - (y_start + box_return.height);
3979 		else
3980 			if(box.y - y_start > parent->height)
3981 				parent->height = box.y - y_start;
3982 	}
3983 	else
3984 		parent->height = box.y - y_start;
3985 
3986 	box.y = 0;
3987 	box.x = 0;
3988 
3989 	/* and restore max_width */
3990 	max_width = max_width_save;
3991 }
3992 
3993 /*****
3994 * Name:
3995 * Return Type:
3996 * Description:
3997 * In:
3998 *
3999 * Returns:
4000 *
4001 *****/
4002 static void
ComputeTableLayout(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement obj_start,XmHTMLObjectTableElement obj_end)4003 ComputeTableLayout(XmHTMLWidget html, PositionBox *box,
4004 	XmHTMLObjectTableElement obj_start, XmHTMLObjectTableElement obj_end)
4005 {
4006 	XmHTMLObjectTableElement tmp, end;
4007 	int max_width_save, bw_save, bh_save;
4008 
4009 	/*****
4010 	* max_width also to be restored, it's final value is governed by the
4011 	* total width of the enclosing table.
4012 	* We also *must* reset the baseline object: it only applies *per*
4013 	* box (= cell). Not resetting it would transfer the baseline object
4014 	* to another cell, which is not very desirable...
4015 	*****/
4016 	max_width_save = max_width;
4017 	had_break = False;
4018 	baseline_obj = NULL;
4019 
4020 	/*****
4021 	* Save width & height of current box. Since we are performing the final
4022 	* table layout, the width & height of the box must stay fixed and may not
4023 	* be modified when we encounter a nested table.
4024 	*****/
4025 	bw_save = box->width;
4026 	bh_save = box->height;
4027 
4028 	for(tmp = obj_start; tmp && tmp != obj_end; tmp = tmp->next)
4029 	{
4030 		switch(tmp->object_type)
4031 		{
4032 			/* collect all words */
4033 			case OBJ_TEXT:
4034 				for(end = tmp; end->next->object_type == OBJ_TEXT;
4035 					end = end->next);
4036 
4037 				/* go and do text layout */
4038 				SetText(html, box, tmp, end->next, False, False);
4039 
4040 				/* back up one element */
4041 				tmp = end;
4042 				break;
4043 
4044 			case OBJ_PRE_TEXT:
4045 				for(end = tmp; end->next->object_type == OBJ_PRE_TEXT;
4046 					end = end->next);
4047 
4048 				/* go and do text layout */
4049 				SetText(html, box, tmp, end->next, True, False);
4050 
4051 				/* back up one element */
4052 				tmp = end;
4053 				break;
4054 			case OBJ_BULLET:
4055 				SetBullet(html, box, tmp);
4056 				break;
4057 			case OBJ_HRULE:
4058 				SetRule(html, box, tmp);
4059 				break;
4060 			case OBJ_TABLE:
4061 				/* nested table hehehehehehe */
4062 				SetBlock(html, box, tmp);
4063 				tmp = SetTable(html, box, tmp);
4064 
4065 				/*****
4066 				* Restore width & height, it might have been modified. This is
4067 				* essential for nested tables: not restoring this will
4068 				* lead to a decreasing width and increasing height of each
4069 				* nested table that needs to be processed and could even
4070 				* lead to a *negative* cell width!
4071 				*****/
4072 				box->width  = bw_save;
4073 				box->height = bh_save;
4074 
4075 				break;
4076 			case OBJ_TABLE_FRAME:
4077 				SetBlock(html, box, tmp);
4078 				break;
4079 			case OBJ_APPLET:
4080 				SetApplet(html, box, tmp);
4081 				SetBreak(html, box, tmp);
4082 				break;
4083 			case OBJ_BLOCK:
4084 				SetBlock(html, box, tmp);
4085 				SetBreak(html, box, tmp);
4086 				break;
4087 			case OBJ_NONE:
4088 				SetBlock(html, box, tmp);
4089 				break;
4090 			default:
4091 				_XmHTMLWarning(__WFUNC__(html, "PreComputeLayout"),
4092 					XMHTML_MSG_78);
4093 		}
4094 	}
4095 	/*****
4096 	* Last object in a tablecell is a valid element (although it is of type
4097 	* OBJ_NONE), so it must be set as well.
4098 	* Not doing so would seriously mess up screen refreshment.
4099 	*****/
4100 	if(obj_end)
4101 		SetBlock(html, box, obj_end);
4102 
4103 	max_width = max_width_save;
4104 }
4105 
4106 /*****
4107 * Name:
4108 * Return Type:
4109 * Description:
4110 * In:
4111 *
4112 * Returns:
4113 *
4114 *****/
4115 static void
CheckVerticalAlignment(XmHTMLWidget html,PositionBox * box,XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,Alignment valign)4116 CheckVerticalAlignment(XmHTMLWidget html, PositionBox *box,
4117 	XmHTMLObjectTableElement start, XmHTMLObjectTableElement end,
4118 	Alignment valign)
4119 {
4120 	/*****
4121 	* INSERT CODE
4122 	* to shift all objects down
4123 	*****/
4124 }
4125 
4126 #ifdef DEBUG
4127 /*
4128 * Routine to dump all objects to file, can be called from within a
4129 * debugger.
4130 */
4131 /* all possible object types */
4132 static String obj_names[] = {"none", "text", "ptext", "bullet",
4133 	"hrule", "table", "tframe", "img", "form", "applet",
4134 	"block"};
4135 
4136 /*****
4137 * Name:			dumpLines
4138 * Return Type: 	void
4139 * Description: 	dumps a list of all XmHTMLObjectTable elements, displaying
4140 *				it's type, coordinates, line number on display and in input
4141 *				file and it's pointer value.
4142 * In:
4143 *	html:		XmHTMLWidget id;
4144 *	file:		name of file to dump to. If "" stdout is used.
4145 * Returns:
4146 *	nothing
4147 *****/
4148 void
dumpLines(XmHTMLWidget html,String file)4149 dumpLines(XmHTMLWidget html, String file)
4150 {
4151 	XmHTMLLineTable *tmp;
4152 	FILE *out = stdout;
4153 	int cnt = 0, nused = 0;
4154 	int i;
4155 	char key;
4156 
4157 	if(HTML_ATTR(line_table) == NULL)
4158 	{
4159 		fprintf(out, "No line table found\n");
4160 		return;
4161 	}
4162 
4163 	if(file != NULL)
4164 	{
4165 		if((out = fopen(file, "w")) == NULL)
4166 		{
4167 			perror(file);
4168 			out = stdout;
4169 			fprintf(out, "Reverting to stdout\n");
4170 		}
4171 		else
4172 			printf("Dumping linetable to %s\n", file);
4173 	}
4174 	else
4175 		fprintf(out, "No output file given, dumping to stdout\n");
4176 
4177 	fprintf(out, "Line\ty\t\tStart Ptr\tEnd Ptr\n");
4178 	fflush(stdout);
4179 
4180 	tmp = HTML_ATTR(line_table);
4181 
4182 	for(i = 0; i < HTML_ATTR(nlines); i++)
4183 	{
4184 		if(tmp[i].used)
4185 		{
4186 			fprintf(out, "%i\t%i\t\t%p\t%p\n",
4187 				i, tmp[i].y, tmp[i].start, tmp[i].end);
4188 			nused++;
4189 		}
4190 		else
4191 			fprintf(out, "%i\t(unused)\n", i);
4192 		cnt++;
4193 		if(out == stdout && !(cnt % 21))
4194 		{
4195 			fprintf(out, "--- Press Q to quit or Return to continue (%i "
4196 				"elements shown ---):", cnt);
4197 			fflush(out);
4198 			key = getchar();
4199 			if(key == 'q' || key == 'Q')
4200 				return;
4201 			fprintf(out, "Line\ty\t\tStart Ptr\tEnd Ptr\n");
4202 		}
4203 	}
4204 	if(out == stdout)
4205 		return;
4206 	fclose(out);
4207 	printf("Done, dumped %i lines of which %i are used\n", cnt, nused);
4208 	fflush(stdout);
4209 }
4210 
4211 /*****
4212 * Name:			checkObjects
4213 * Return Type: 	void
4214 * Description: 	checks the entire XmHTMLObjectTable element list for elements
4215 *				with zero coordinates and/or display line number.
4216 * In:
4217 *	html:		XmHTMLWidget id;
4218 * Returns:
4219 *	nothing.
4220 *****/
4221 void
checkObjects(XmHTMLWidget html)4222 checkObjects(XmHTMLWidget html)
4223 {
4224 	XmHTMLObjectTable *tmp;
4225 	int cnt = 0, err = 0;
4226 	int nl = 0;
4227 	printf("Checking for objects with zero coordinates and/or linenumber.\n");
4228 
4229 	nl = HTML_ATTR(formatted)->line;
4230 
4231 	for(tmp = HTML_ATTR(formatted); tmp != NULL; tmp = tmp->next)
4232 	{
4233 		if(tmp->x == 0 && tmp->y == 0)
4234 		{
4235 			printf("Object %s %p has zero coordinates\n",
4236 				obj_names[tmp->object_type], tmp);
4237 			err++;
4238 		}
4239 		if(tmp->line == 0 && tmp->y == 0)
4240 		{
4241 			printf("Object %s %p has zero line number\n",
4242 				obj_names[tmp->object_type], tmp);
4243 			err++;
4244 		}
4245 
4246 		if(tmp->line < nl)
4247 			printf("Object %s %p: bad line number: got %i, expected %i or "
4248 				"more\n", obj_names[tmp->object_type], tmp, tmp->line, nl);
4249 		if(tmp->line > nl)
4250 			nl = tmp->line;
4251 		cnt++;
4252 	}
4253 	printf("Done, checked %i objects, %i errors found\n", cnt, err);
4254 }
4255 
4256 /*****
4257 * Name:			dumpLines
4258 * Return Type: 	void
4259 * Description: 	dumps a list of all XmHTMLObjectTable elements, displaying
4260 *				it's type, coordinates, line number on display and in input
4261 *				file and it's pointer value.
4262 * In:
4263 *	html:		XmHTMLWidget id;
4264 *	file:		name of file to dump to. If "" stdout is used.
4265 * Returns:
4266 *	nothing
4267 *****/
4268 void
dumpObjects(XmHTMLWidget html,String file)4269 dumpObjects(XmHTMLWidget html, String file)
4270 {
4271 	XmHTMLObjectTable *tmp;
4272 	FILE *out = stdout;
4273 	int cnt = 0;
4274 	char key;
4275 
4276 	if(file != NULL)
4277 	{
4278 		if((out = fopen(file, "w")) == NULL)
4279 		{
4280 			perror(file);
4281 			out = stdout;
4282 			fprintf(out, "Reverting to stdout\n");
4283 		}
4284 		else
4285 			printf("Dumping objecttable to %s\n", file);
4286 	}
4287 	else
4288 		fprintf(out, "No output file given, dumping to stdout\n");
4289 
4290 	fprintf(out, "Object\t\tx\ty\tw\th\tline\tinput line\tObject Ptr\n");
4291 	fflush(stdout);
4292 
4293 	for(tmp = HTML_ATTR(formatted); tmp != NULL; tmp = tmp->next)
4294 	{
4295 		if(tmp->object)
4296 			fprintf(out, "%s\t\t%i\t%i\t%i\t%i\t%i\t%i\t\t%p\n",
4297 				obj_names[tmp->object_type],
4298 				tmp->x, tmp->y, tmp->width, tmp->height, tmp->line,
4299 				tmp->object->line, tmp);
4300 		else
4301 			fprintf(out, "%s\t\t%i\t%i\t%i\t%i\t%i\t(?)\t\t%p\n",
4302 				obj_names[tmp->object_type], tmp->x, tmp->y, tmp->width,
4303 				tmp->height, tmp->line, tmp);
4304 		cnt++;
4305 		if(out == stdout && !(cnt % 21))
4306 		{
4307 			fprintf(out, "--- Press Q to quit or Return to continue (%i "
4308 				"elements shown ---):", cnt);
4309 			fflush(out);
4310 			key = getchar();
4311 			if(key == 'q' || key == 'Q')
4312 				return;
4313 			fprintf(out, "Object\t\tx\ty\t\tw\th\tline\tinput line\tObject Ptr\n");
4314 		}
4315 	}
4316 	if(out == stdout)
4317 		return;
4318 	fclose(out);
4319 	printf("Done, dumped %i objects\n", cnt);
4320 	fflush(stdout);
4321 }
4322 #endif
4323