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