1 /*****
2 * paint.c : XmHTML rendering routines
3 *
4 * This file Version	$Revision: 1.19 $
5 *
6 * Creation date:		Fri Dec  6 19:50:20 GMT+0100 1996
7 * Last modification: 	$Date: 1998/04/27 07:02:05 $
8 * By:					$Author: newt $
9 * Current State:		$State: Exp $
10 *
11 * Author:				newt
12 * (C)Copyright 1995-1996 Ripley Software Development
13 * All Rights Reserved
14 *
15 * This file is part of the XmHTML Widget Library.
16 *
17 * This library is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU Library General Public
19 * License as published by the Free Software Foundation; either
20 * version 2 of the License, or (at your option) any later version.
21 *
22 * This library is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25 * Library General Public License for more details.
26 *
27 * You should have received a copy of the GNU Library General Public
28 * License along with this library; if not, write to the Free
29 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 *
31 *****/
32 /*****
33 * ChangeLog
34 * $Log: paint.c,v $
35 * Revision 1.19  1998/04/27 07:02:05  newt
36 * A few bugfixes in anchor painting + tka stuff
37 *
38 * Revision 1.18  1998/04/04 06:28:17  newt
39 * XmHTML Beta 1.1.3
40 *
41 * Revision 1.17  1997/10/23 00:25:06  newt
42 * XmHTML Beta 1.1.0 release
43 *
44 * Revision 1.16  1997/08/31 17:37:20  newt
45 * log edit
46 *
47 * Revision 1.15  1997/08/30 01:17:29  newt
48 * Text layout for <PRE></PRE> is now properly handled.
49 * Added anchor highlighting support.
50 * Generalized anchor button rendering.
51 * Added support for colored <HR>.
52 *
53 * Revision 1.14  1997/08/01 13:05:28  newt
54 * Lots of bugfixes: baseline adjustment of images, forms components or large
55 * fonts, animated gif disposal handling. Reduced data storage.
56 *
57 * Revision 1.13  1997/05/28 01:52:43  newt
58 * Bugfix 05/26/97-01. Form layout changes.
59 *
60 * Revision 1.12  1997/04/29 14:30:19  newt
61 * Bugfix 04/26/97-01. Fixed SetText to use dimensions of baseline word when
62 * updating the object data.
63 *
64 * Revision 1.11  1997/04/03 05:40:21  newt
65 * Anchor and word rendering of mixed fonts finally works. Fixed anchor
66 * transparency for documents with a body images. Fixed image anchor scrolling
67 * problem.
68 *
69 * Revision 1.10  1997/03/28 07:18:37  newt
70 * Alignment bugfix: now only done with a positive offset.
71 * Bugfix in frame disposal: index was one frame too early.
72 * Bugfix in image anchor rendering: now done before checking exposure region.
73 *
74 * Revision 1.9  1997/03/20 08:12:38  newt
75 * SetText: images now use their vertical alignment specs for baseline
76 * adjustment.
77 *
78 * Revision 1.8  1997/03/11 19:57:04  newt
79 * SetText now does both text and image layout.
80 * DrawImage now does animated gifs and transparent images
81 *
82 * Revision 1.7  1997/03/04 18:48:52  newt
83 * Animation stuff. Fixed a spacing bug in SetText
84 *
85 * Revision 1.6  1997/03/04 01:00:55  newt
86 * ?
87 *
88 * Revision 1.5  1997/03/02 23:20:31  newt
89 * Added setting and rendering of images. image-related changes to SetText.
90 * SetText now also ``glues'' words together properly
91 *
92 * Revision 1.4  1997/02/11 02:09:28  newt
93 * Fair amount of changes: text layout has been completely changed
94 *
95 * Revision 1.3  1997/01/09 06:55:47  newt
96 * expanded copyright marker
97 *
98 * Revision 1.2  1997/01/09 06:46:39  newt
99 * lots of changes: painting now divided in a layout en rendering part.
100 *
101 * Revision 1.1  1996/12/19 02:17:12  newt
102 * Initial Revision
103 *
104 *****/
105 #ifdef HAVE_CONFIG_H
106 #include "config.h"
107 #endif
108 
109 #include <stdlib.h>
110 #include <stdio.h>
111 
112 #include <X11/Xlib.h>
113 #ifdef HAVE_XFT
114 #include <X11/Xft/Xft.h>
115 #endif
116 
117 #include "toolkit.h"
118 #include XmHTMLPrivateHeader
119 
120 /*** External Function Prototype Declarations ***/
121 
122 /*** Public Variable Declarations ***/
123 
124 /*** Private Datatype Declarations ****/
125 
126 /*** Private Function Prototype Declarations ****/
127 /* main rendering routines */
128 static void DrawText(XmHTMLWidget html, XmHTMLObjectTableElement data);
129 static void DrawAnchor(XmHTMLWidget html, XmHTMLObjectTableElement data);
130 static void DrawImageAnchor(XmHTMLWidget html, XmHTMLObjectTableElement data);
131 static void DrawRule(XmHTMLWidget html, XmHTMLObjectTableElement data);
132 static void DrawBullet(XmHTMLWidget html, XmHTMLObjectTableElement data);
133 static XmHTMLObjectTableElement DrawTable(XmHTMLWidget html,
134 	XmHTMLObjectTableElement data, XmHTMLObjectTableElement data_end, Bool inNestedTable);
135 
136 /* various helper routines */
137 static void DrawAnchorButton(ToolkitAbstraction *tka, int x, int y,
138 	Dimension width, Dimension height, GC top, GC bottom);
139 static void DrawFrame(XmHTMLWidget html, XmHTMLImage *image, int xs, int ys);
140 static void DrawTableBorder(XmHTMLWidget html, XmHTMLTable *table);
141 static void DrawCellFrame(XmHTMLWidget html, TableCell *cell);
142 static void DrawCellContent(XmHTMLWidget html, XmHTMLObjectTableElement start,
143 	XmHTMLObjectTableElement end, Bool inNestedTable);
144 static void TimerCB(XtPointer data, XtIntervalId *id);
145 
146 /*** Private Variable Declarations ***/
147 #define DRAW_TOP		CELL_TOP
148 #define DRAW_BOTTOM		CELL_BOTTOM
149 #define DRAW_LEFT		CELL_LEFT
150 #define DRAW_RIGHT		CELL_RIGHT
151 #define DRAW_BOX		CELL_BOX
152 #define DRAW_NONE		CELL_NONE
153 
154 /*****
155 * Same note as for anchor width adjustment applies here to, except that we
156 * _must_ do this locally instead of adjusting the width field of a word
157 * directly. The width field is used by the line breaking algorithm in SetText,
158 * so modifying it will make the words wider every time the widget is resized.
159 * Luckily this is only required for underlined/striked words.
160 *****/
161 #define AdjustWordWidth { \
162 	width = words[i].width; \
163 	if(i < nwords-1 && words[i].line == words[i+1].line) { \
164 			width = words[i+1].x - words[i].x; \
165 	} \
166 }
167 
168 /********
169 **** Private paint functions (display independent)
170 ********/
171 
172 /*****
173 * Name:			DrawText
174 * Return Type: 	void
175 * Description: 	main text rendering engine.
176 * In:
177 *	html:		XmHTMLWidget id;
178 *	data:		element to be painted;
179 * Returns:
180 *	nothing.
181 * Note:
182 *	Used for both regular and preformatted text.
183 *****/
184 static void
DrawText(XmHTMLWidget html,XmHTMLObjectTableElement data)185 DrawText(XmHTMLWidget html, XmHTMLObjectTableElement data)
186 {
187 	int width, ys, xs, nwords = data->n_words;
188 	XmHTMLWord *words = data->words;
189 	GC gc = HTML_ATTR(gc);
190 	ToolkitAbstraction *tka = HTML_ATTR(tka);
191 	register int i;
192 	register XmHTMLWord *tmp;
193 
194 	if(!nwords)
195 		return;
196 
197 	/* only need to set this once */
198 	tka->SetForeground(tka->dpy, gc, data->fg);
199 
200 	for(i = 0 ; i < nwords; i++)
201 	{
202 		tmp = &words[i];
203 
204 		/*
205 		* When any of the two cases below is true, the text at the current
206 		* position is outside the exposed area. Not doing this check would
207 		* cause a visible flicker of the screen when scrolling: the entire
208 		* line would be repainted, even the invisible text.
209 		* And we sure don't want to render any linebreaks, looks pretty ugly.
210 		* (this test is a lot cheaper than doing ``invisible'' rendering)
211 		*/
212 		if((HTML_ATTR(paint_y) > tmp->y + tmp->height ||
213 			HTML_ATTR(paint_height) < tmp->y) ||
214 			(HTML_ATTR(paint_x) > tmp->x + tmp->width ||
215 			HTML_ATTR(paint_width) < tmp->x) || tmp->type == OBJ_BLOCK)
216 		{
217 #ifdef DEBUG
218 			if(HTML_ATTR(paint_y) > tmp->y + tmp->height ||
219 				HTML_ATTR(paint_height) < tmp->y)
220 				_XmHTMLDebug(16, ("paint.c: DrawText, skipping %s, outside "
221 					"vertical range.\n", tmp->word));
222 			else if(tmp->type != OBJ_BLOCK)
223 				_XmHTMLDebug(16, ("paint.c: DrawText, skipping %s, outside "
224 					"horizontal range.\n", tmp->word));
225 			else
226 				_XmHTMLDebug(16, ("paint.c: DrawText, skipping break.\n"));
227 #endif
228 			continue;
229 		}
230 
231 		xs = tmp->x - HTML_ATTR(scroll_x);
232 		ys = tmp->y - HTML_ATTR(scroll_y);
233 
234 		tka->DrawString(tka, words[0].font, gc, xs, ys,
235 			tmp->word, tmp->len);
236 
237 		if(tmp->line_data & LINE_UNDER)
238 		{
239 			int dy;
240 			/*
241 			* vertical position for underline, barely connects with the
242 			* underside of the ``deepest'' character.
243 			*/
244 			dy = ys + tmp->base->font->ul_offset;
245 			AdjustWordWidth;
246 
247 			tka->SetLineAttributes(tka->dpy, gc,
248 				tmp->base->font->ul_thickness,
249 				(tmp->line_data & LINE_SOLID ?
250 					tka->line_style[GC_LINE_SOLID] :
251 					tka->line_style[GC_LINE_DOUBLE_DASH]),
252 				tka->cap_style[GC_CAP_BUTT],
253 				tka->join_style[GC_JOIN_BEVEL]);
254 
255 			tka->DrawLine(tka->dpy, tka->win, gc, xs, dy, xs + width, dy);
256 			if(tmp->line_data & LINE_DOUBLE)
257 				tka->DrawLine(tka->dpy, tka->win, gc, xs, dy+2, xs + width,
258 					dy+2);
259 		}
260 		if(tmp->line_data & LINE_STRIKE)
261 		{
262 			int dy;
263 			/* strikeout line is somewhere near the middle of a line */
264 			dy = ys - tmp->base->font->st_offset;
265 			AdjustWordWidth;
266 
267 			tka->SetLineAttributes(tka->dpy, gc,
268 				tmp->base->font->st_thickness,
269 				tka->line_style[GC_LINE_SOLID],
270 				tka->cap_style[GC_CAP_BUTT],
271 				tka->join_style[GC_JOIN_BEVEL]);
272 			tka->DrawLine(tka->dpy, tka->win, gc, xs, dy, xs + width, dy);
273 		}
274 	}
275 }
276 
277 /*****
278 * Name:			DrawAnchor
279 * Return Type: 	void
280 * Description: 	main text anchor renderer.
281 * In:
282 *	html:		XmHTMLWidget id;
283 *	data:		anchor to be painted;
284 * Returns:
285 *	nothing.
286 * Note:
287 *	This routine handles all textual anchor stuff. It paints anchors according
288 *	to the selected anchor style and (optionally) performs anchor highlighting.
289 *	Image anchors are rendered by DrawImageAnchor.
290 *****/
291 static void
DrawAnchor(XmHTMLWidget html,XmHTMLObjectTableElement data)292 DrawAnchor(XmHTMLWidget html, XmHTMLObjectTableElement data)
293 {
294 	int x, xs, y, ys, width, start, nwords = 0;
295 	GC gc = HTML_ATTR(gc);
296 	GC hiGC, topGC, bottomGC;
297 	XmHTMLfont *font;
298 	register int i, j;
299 	XmHTMLWord **all_words = NULL, *words = NULL, *tmp;
300 	XmHTMLObjectTableElement a_start, a_end, temp;
301 	ToolkitAbstraction *tka = HTML_ATTR(tka);
302 
303 	/* pick up the real start of this anchor */
304 	for(a_start = data; a_start && a_start->anchor == data->anchor;
305 		a_start = a_start->prev);
306 
307 	/* sanity, should never happen */
308 	if(a_start == NULL)
309 	{
310 		_XmHTMLWarning(__WFUNC__(html, "DrawAnchor"), XMHTML_MSG_90);
311 		return;
312 	}
313 
314 	/* previous loop always walks back one to many */
315 	a_start = a_start->next;
316 
317 	/* pick up the real end of this anchor and count the words */
318 	for(a_end = a_start, nwords = 0;
319 		a_end != NULL && a_end->anchor == a_start->anchor; a_end = a_end->next)
320 	{
321 		/* ignore image words, they get handled by DrawImageAnchor. */
322 		if(!(a_end->text_data & TEXT_IMAGE) &&
323 			!(a_end->text_data & TEXT_BREAK))
324 			nwords += a_end->n_words;
325 	}
326 
327 	_XmHTMLDebug(16, ("paint.c: DrawAnchor, anchor contains %i words\n",
328 		nwords));
329 
330 	/* sanity check */
331 	if(!nwords)
332 		return;		/* fix 01/30/97-01, kdh */
333 
334 	/*
335 	* put all anchor words into an array if this anchor spans multiple
336 	* objects (as can be the case with font changes within an anchor)
337 	* If this isn't the case, just use the words of the current data
338 	* object.
339 	*/
340 	if(a_start->next != a_end)
341 	{
342 		_XmHTMLDebug(16, ("paint.c: DrawAnchor, allocating a word array for "
343 			"%i words\n", nwords));
344 
345 		all_words = (XmHTMLWord**)calloc(nwords, sizeof(XmHTMLWord*));
346 
347 		i = 0;
348 		for(temp = a_start; temp != a_end; temp = temp->next)
349 		{
350 			/* ignore image words, they get handled by DrawImageAnchor. */
351 			if(!(temp->text_data & TEXT_IMAGE) &&
352 				!(temp->text_data & TEXT_BREAK))
353 			{
354 				for(j = 0 ; j < temp->n_words; j++)
355 					all_words[i++] = &(temp->words[j]);
356 			}
357 		}
358 		words = NULL;
359 	}
360 	else
361 	{
362 		_XmHTMLDebug(16, ("paint.c: DrawAnchor, not allocating a word array, "
363 			"all words belong to the same object.\n"));
364 		words = data->words;
365 	}
366 
367 	/*
368 	* this is used for drawing the bounding rectangle of an anchor.
369 	* When an anchor is encountered, width is used to compute the total
370 	* width of a rectangle surrounding all anchor words on the same line.
371 	* The bounding rectangle drawn extends a little bit to the left and
372 	* right of the anchor.
373 	*/
374 	width = (words ? words[0].width : all_words[0]->width);
375 	/* extend to the left */
376 	x = (words ? words[0].x : all_words[0]->x) - HTML_ATTR(scroll_x) - 2;
377 	y = (words ? words[0].y : all_words[0]->y) - HTML_ATTR(scroll_y);
378 	i = start = 0;
379 
380 	do
381 	{
382 		tmp = (words ? &words[i] : all_words[i]);
383 
384 		/* anchors are always painted */
385 		ys = tmp->y - HTML_ATTR(scroll_y);
386 		xs = tmp->x - HTML_ATTR(scroll_x);
387 
388 		/* baseline font */
389 		font = tmp->base->font;
390 
391 		_XmHTMLFullDebug(16, ("paint.c: painting anchor, x = %i, y = %i\n",
392 			tmp->x, tmp->y));
393 
394 		/* compute total width of all words on this line */
395 		if(ys == y)
396 			width = xs + tmp->width - x;
397 		/* extend to the right if this word has a trailing space */
398 		width += (tmp->posbits & TEXT_SPACE_TRAIL ? 2 : 1);
399 
400 		if(ys != y || i == nwords-1)
401 		{
402 			/*****
403 			* Anchor painting. We first make the distinction between documents
404 			* with and without a body image.
405 			* Then we check if we need to paint the anchors as pushbuttons
406 			* and then we check if we are to perform anchor highlighting.
407 			*****/
408 			if(HTML_ATTR(body_image) == NULL)
409 			{
410 				/*****
411 				* No body image present.
412 				* Check if we are to paint the anchors as pushbuttons.
413 				*****/
414 				if(HTML_ATTR(anchor_buttons))
415 				{
416 					if(data->anchor_state == ANCHOR_INSELECT &&
417 						HTML_ATTR(highlight_on_enter))
418 					{
419 						/* can only happen if XmNhighlightOnEnter is set */
420 						hiGC = MGR_ATTR(highlight_GC);
421 						topGC = MGR_ATTR(top_shadow_GC);
422 						bottomGC = MGR_ATTR(bottom_shadow_GC);
423 					}
424 					else if(data->anchor_state == ANCHOR_SELECTED)
425 					{
426 						hiGC = MGR_ATTR(highlight_GC);
427 						topGC = MGR_ATTR(bottom_shadow_GC);
428 						bottomGC = MGR_ATTR(top_shadow_GC);
429 					}
430 					else	/* button is unselected */
431 					{
432 						/* use object's background */
433 						tka->SetForeground(tka->dpy, gc, data->bg);
434 						hiGC = gc;
435 						topGC = MGR_ATTR(top_shadow_GC);
436 						bottomGC = MGR_ATTR(bottom_shadow_GC);
437 					}
438 					if(HTML_ATTR(highlight_on_enter))
439 					{
440 						/* paint button highlighting */
441 						tka->FillRectangle(tka->dpy, tka->win, hiGC, x,
442 							y - font->ascent, width, font->lineheight);
443 					}
444 					/* and draw as requested */
445 					DrawAnchorButton(tka, x, y - font->ascent,
446 						width, font->lineheight, topGC, bottomGC);
447 				}
448 				else
449 				{
450 					/*****
451 					* No anchor buttons. Determine which color to use to paint
452 					* the bounding box around the anchor.
453 					* Note: without a body image, anchor highlighting is
454 					* achieved by painting the bounding box in the highlight
455 					* color.
456 					*****/
457 					if(data->anchor_state == ANCHOR_INSELECT)
458 						tka->SetForeground(tka->dpy, gc,
459 								MGR_ATTR(highlight_color));
460 					else if(data->anchor_state == ANCHOR_SELECTED)
461 						tka->SetForeground(tka->dpy, gc,
462 								HTML_ATTR(anchor_activated_bg));
463 					else
464 						tka->SetForeground(tka->dpy, gc, data->bg);
465 
466 					tka->FillRectangle(tka->dpy, tka->win, gc, x,
467 						y - font->ascent, width, font->lineheight);
468 				}
469 				/* set appropriate foreground color */
470 				tka->SetForeground(tka->dpy, gc,
471 					data->anchor_state == ANCHOR_SELECTED ?
472 					HTML_ATTR(anchor_activated_fg) : data->fg);
473 			}
474 			else
475 			{
476 				/*****
477 				* We have a body image. Painting the buttons as above would
478 				* obliterate the part of the image under the anchor, so
479 				* if we are to perform button highlighting, we paint the
480 				* anchor's *text* in the highlight color.
481 				*****/
482 				if(HTML_ATTR(anchor_buttons))
483 				{
484 					if(data->anchor_state == ANCHOR_SELECTED)
485 					{
486 						topGC = MGR_ATTR(bottom_shadow_GC),
487 						bottomGC = MGR_ATTR(top_shadow_GC);
488 					}
489 					else	/* button is unselected or being selected */
490 					{
491 						topGC = MGR_ATTR(top_shadow_GC),
492 						bottomGC = MGR_ATTR(bottom_shadow_GC);
493 					}
494 					/* draw as requested */
495 					DrawAnchorButton(tka, x, y - font->ascent,
496 						width, font->lineheight, topGC, bottomGC);
497 				}
498 				/*****
499 				* no special stuff for anchor bounding box if we aren't
500 				* painting the anchors as buttons.
501 				*****/
502 				/* set appropriate foreground color */
503 				if(data->anchor_state == ANCHOR_INSELECT)
504 					tka->SetForeground(tka->dpy, gc,
505 						MGR_ATTR(highlight_color));
506 				else if(data->anchor_state == ANCHOR_SELECTED)
507 					tka->SetForeground(tka->dpy, gc,
508 							HTML_ATTR(anchor_activated_fg));
509 				else
510 					tka->SetForeground(tka->dpy, gc, data->fg);
511 			}
512 			/*
513 			* paint all text. Need to do it here since the XFillRect call
514 			* would obliterate any text painted before it.
515 			*/
516 
517 			if(words)
518 			{
519 				for(j = start; j < i+1; j++)
520 				{
521 					tka->DrawString(tka, words[j].font, gc,
522 						words[j].x - HTML_ATTR(scroll_x),
523 						words[j].y - HTML_ATTR(scroll_y),
524 						words[j].word, words[j].len);
525 				}
526 			}
527 			else
528 			{
529 				for(j = start; j < i+1; j++)
530 				{
531 					tka->DrawString(tka, all_words[j]->font, gc,
532 						all_words[j]->x - HTML_ATTR(scroll_x),
533 						all_words[j]->y - HTML_ATTR(scroll_y),
534 						all_words[j]->word, all_words[j]->len);
535 				}
536 			}
537 
538 			/* Anchor buttons are never underlined, it looks ugly */
539 			if(!HTML_ATTR(anchor_buttons) &&
540 				(tmp->line_data & LINE_SOLID || tmp->line_data & LINE_DASHED))
541 			{
542 				int dy = y + font->ul_offset;
543 				tka->SetLineAttributes(tka->dpy, gc, 1,
544 					(tmp->line_data & LINE_SOLID ?
545 						tka->line_style[GC_LINE_SOLID] :
546 						tka->line_style[GC_LINE_DOUBLE_DASH]),
547 					tka->cap_style[GC_CAP_BUTT],
548 					tka->join_style[GC_JOIN_BEVEL]);
549 				tka->DrawLine(tka->dpy, tka->win, gc, x+2, dy, x+width, dy);
550 				/* draw another line if requested */
551 				if(tmp->line_data & LINE_DOUBLE)
552 					tka->DrawLine(tka->dpy, tka->win, gc, x + 2, dy + 2,
553 						x + width, dy + 2);
554 			}
555 			if(tmp->line_data & LINE_STRIKE)
556 			{
557 				int dy = y - font->st_offset;
558 				tka->SetLineAttributes(tka->dpy, gc, 1,
559 						tka->line_style[GC_LINE_SOLID],
560 						tka->cap_style[GC_CAP_BUTT],
561 						tka->join_style[GC_JOIN_BEVEL]);
562 				tka->DrawLine(tka->dpy, tka->win, gc, x+2, dy,
563 						x + width - 2, dy);
564 			}
565 			/* stupid hack to get the last word of a broken anchor right */
566 			if(ys != y && i == nwords-1)
567 				i--;
568 			/* next word starts on another line */
569 			width = tmp->width;
570 			start = i;
571 			x = xs-2;
572 			y = ys;
573 		}
574 		i++;
575 	}
576 	while(i != nwords);
577 
578 	if(words == NULL)
579 	{
580 		_XmHTMLDebug(16, ("paint.c: DrawAnchor, freeing allocated word "
581 			"array.\n"));
582 		free(all_words);	/* fix 05/26/97-01, kdh */
583 		/*****
584 		* adjust current object data as we have now updated a number of
585 		* objects in one go. We must use prev as _XmHTMLPaint will advance
586 		* to a_end itself.
587 		*****/
588 		data = (a_end ? a_end->prev : data);
589 	}
590 
591 	/* flush out referrering hyperlink if we have a DrawAnchorData function */
592 	if(tka->DrawAnchorData)
593 	{
594 		nwords = data->n_words - 1;
595 
596 		/*****
597 		* compute position for footnote marker: at the end of the last word
598 		* in this hyperlink. Currently only used by postscript output.
599 		*****/
600 		x = data->words[nwords].x + data->words[nwords].width;
601 		y = data->words[nwords].y;
602 
603 		/* store */
604 		tka->DrawAnchorData(tka->dpy, tka->win, HTML_ATTR(gc), x, y, data);
605 
606 	}
607 }
608 
609 static void
DrawImageAnchor(XmHTMLWidget html,XmHTMLObjectTableElement data)610 DrawImageAnchor(XmHTMLWidget html, XmHTMLObjectTableElement data)
611 {
612 	int x, y, width, height;
613 	GC gc = HTML_ATTR(gc);
614 	ToolkitAbstraction *tka = HTML_ATTR(tka);
615 
616 	/*
617 	* this is used for drawing the bounding rectangle of an anchor.
618 	* When an anchor is encountered, width is used to compute the total
619 	* width of a rectangle surrounding all anchor words on the same line.
620 	* The bounding rectangle drawn extends a little bit to the left and
621 	* right of the anchor.
622 	*/
623 	width  = data->words->width + 2;
624 	height = data->words->height + 2;
625 
626 	/* extend to the left */
627 	x = data->words->x - HTML_ATTR(scroll_x) - 1;
628 	/* and to the top */
629 	y = data->words->y - HTML_ATTR(scroll_y) - 1;
630 
631 	if(data->words->image->border)
632 	{
633 		/* add border offsets as well */
634 		x -= data->words->image->border;
635 		y -= data->words->image->border;
636 
637 		_XmHTMLFullDebug(16, ("paint.c: painting image anchor, x = %i, "
638 			"y = %i\n", data->x, data->y));
639 
640 		if(HTML_ATTR(anchor_buttons))
641 		{
642 			GC top, bottom;
643 			if(HTML_ATTR(highlight_on_enter))
644 			{
645 				/* can only happen if XmNhighlightOnEnter is set */
646 				if(data->anchor_state == ANCHOR_INSELECT)
647 				{
648 					/* paint button highlighting */
649 					if(HTML_ATTR(body_image) == NULL &&
650 						data->words->image->clip != None)
651 						tka->FillRectangle(tka->dpy, tka->win,
652 							MGR_ATTR(highlight_GC), x, y , width, height);
653 					/* and draw as unselected */
654 					top    = MGR_ATTR(top_shadow_GC);
655 					bottom = MGR_ATTR(bottom_shadow_GC);
656 				}
657 				else if(data->anchor_state == ANCHOR_SELECTED)
658 				{
659 					/* paint button highlighting */
660 					if(HTML_ATTR(body_image) == NULL &&
661 						data->words->image->clip != None)
662 						tka->FillRectangle(tka->dpy, tka->win,
663 							MGR_ATTR(highlight_GC), x, y, width, height);
664 
665 					/* and draw as selected */
666 					top    = MGR_ATTR(bottom_shadow_GC);
667 					bottom = MGR_ATTR(top_shadow_GC);
668 				}
669 				else	/* button is unselected */
670 				{
671 					/* restore correct background */
672 					if(HTML_ATTR(body_image) == NULL &&
673 						data->words->image->clip != None)
674 					{
675 						tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_bg));
676 						tka->FillRectangle(tka->dpy, tka->win, gc, x, y,
677 								width, height);
678 						tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_fg));
679 					}
680 					/* and draw as unselected */
681 					top    = MGR_ATTR(top_shadow_GC);
682 					bottom = MGR_ATTR(bottom_shadow_GC);
683 				}
684 			}
685 			else 	/* either selected or unselected */
686 			{
687 				if(data->anchor_state == ANCHOR_SELECTED)
688 				{
689 					top = MGR_ATTR(bottom_shadow_GC);
690 					bottom = MGR_ATTR(top_shadow_GC);
691 				}
692 				else
693 				{
694 					bottom = MGR_ATTR(bottom_shadow_GC);
695 					top = MGR_ATTR(top_shadow_GC);
696 				}
697 			}
698 			DrawAnchorButton(tka, x, y, width, height, top, bottom);
699 		}
700 		else
701 		{
702 			/* set line attribs */
703 			tka->SetLineAttributes(tka->dpy, gc, data->words->image->border,
704 					tka->line_style[GC_LINE_SOLID],
705 					tka->cap_style[GC_CAP_BUTT],
706 					tka->join_style[GC_JOIN_BEVEL]);
707 
708 			/* draw background */
709 			/* fix 04/03/97-01, kdh */
710 			if(HTML_ATTR(body_image) == NULL)
711 			{
712 				if(data->anchor_state == ANCHOR_INSELECT)
713 					tka->SetForeground(tka->dpy, gc,
714 							MGR_ATTR(highlight_color));
715 				else if(data->anchor_state == ANCHOR_SELECTED)
716 					tka->SetForeground(tka->dpy, gc,
717 							HTML_ATTR(anchor_activated_bg));
718 				else
719 					tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_bg));
720 				tka->FillRectangle(tka->dpy, tka->win, gc, x, y,
721 						width, height);
722 			}
723 
724 			/* draw lines */
725 			tka->SetForeground(tka->dpy, gc,
726 				data->anchor_state == ANCHOR_SELECTED ?
727 					HTML_ATTR(anchor_activated_fg) : HTML_ATTR(anchor_fg));
728 			tka->DrawRectangle(tka->dpy, tka->win, gc, x, y, width, height);
729 		}
730 	}
731 	/* paint the alt text if images are disabled */
732 	if(!HTML_ATTR(images_enabled))
733 	{
734 		tka->SetForeground(tka->dpy, gc,
735 				data->anchor_state == ANCHOR_SELECTED ?
736 					HTML_ATTR(anchor_activated_fg) : HTML_ATTR(anchor_fg));
737 
738 		/* put text inside bounding rectangle */
739 		x += data->words->image->width + 4;
740 		y += data->words->image->height/2 + 4;
741 
742 		tka->DrawString(tka, data->words->font, gc, x, y,
743 			data->words->word, data->words->len);
744 	}
745 }
746 
747 /*
748 * To prevent racing conditions, we must first remove an
749 * existing timeout proc before we add a new one.
750 */
751 #define REMOVE_TIMEOUTPROC(IMG) { \
752 	if(IMG->proc_id) \
753 	{ \
754 		_XmHTMLDebug(16, ("paint.c: DrawImage, removing animation %s " \
755 			"timeout\n", IMG->url)); \
756 		tka->RemoveTimeOut(IMG->proc_id); \
757 		IMG->proc_id = None; \
758 	} \
759 }
760 
761 /*****
762 * Name:			TimerCB
763 * Return Type: 	void
764 * Description: 	Xt timer callback procedure. Used by animations
765 * In:
766 *	data:		animation for which this timeout has been called;
767 *	id:			id of timer used to activate this function.
768 * Returns:
769 *	nothing.
770 *****/
771 static void
TimerCB(XtPointer data,XtIntervalId * id)772 TimerCB(XtPointer data, XtIntervalId *id)
773 {
774 	XmHTMLImage *image = (XmHTMLImage*)data;
775 
776 	/* freeze animation at current frame */
777 	if(image->html->html.freeze_animations)
778 	{
779 		ToolkitAbstraction *tka = image->html->html.tka;
780 		REMOVE_TIMEOUTPROC(image);
781 		return;
782 	}
783 
784 	image->options |= IMG_FRAMEREFRESH;
785 	_XmHTMLDrawImage(image->html, image->owner, 0, True);
786 }
787 
788 #define RESET_GC(MYGC) { \
789 	tka->SetClipOriginAndMask(tka, MYGC, 0, 0, None); \
790 }
791 
792 #define GET_TILE_OFFSETS(TSX,TSY) { \
793 	int tile_width, tile_height, x_dist, y_dist; \
794 	int ntiles_x, ntiles_y; \
795 	int x_offset, y_offset, xd, yd; \
796 	/* adjust for logical screen offsets */ \
797 	xd = xs + fx; /* x-pos relative to upper-left screen corner */ \
798 	yd = ys + fy; /* y-pos relative to upper-left screen corner */ \
799 	tile_width  = HTML_ATTR(body_image)->width; \
800 	tile_height = HTML_ATTR(body_image)->height; \
801 	x_dist = HTML_ATTR(scroll_x) + xd; /* total distance covered so far */ \
802 	y_dist = HTML_ATTR(scroll_y) + yd; /* total distance covered so far */ \
803 	ntiles_x = (int)(x_dist/tile_width); /* no of horizontal tiles */ \
804 	ntiles_y = (int)(y_dist/tile_height); /* no of vertical tiles */ \
805 	x_offset = x_dist - (ntiles_x * tile_width); \
806 	y_offset = y_dist - (ntiles_y * tile_height); \
807 	TSX = xd - x_offset; \
808 	TSY = yd - y_offset; \
809 }
810 
811 /*****
812 * Name: 		DrawFrame
813 * Return Type: 	void
814 * Description: 	animation driver, does frame disposal and renders a new
815 *				frame
816 * In:
817 *	w:			XmHTMLWidget id
818 *	image:		image data
819 *	xs:			absolute screen x-coordinate
820 *	ys:			absolute screen y-coordinate
821 * Returns:
822 *	nothing
823 * Note:
824 *	Complex animations
825 *	------------------
826 *	Instead of drawing into the window directly, we draw into an internal
827 *	pixmap and blit this pixmap to the screen when all required processing
828 *	has been done, which is a lot faster than drawing on the screen directly.
829 *	Another advantage of this approach is that we always have a current state
830 *	available which can be used when an animation is scrolled on and off
831 *	screen (frame dimensions differ from logical screen dimensions or a
832 *	disposal method other than XmIMAGE_DISPOSE_NONE is to be used).
833 *
834 *	Easy animations
835 *   ---------------
836 *	Each frame is blitted to screen directly, only processing done is using a
837 *	possible clipmask (frame dimensions equal to logical screen dimensions and
838 *	a disposal method of XmIMAGE_DISPOSE_NONE).
839 *****/
840 static void
DrawFrame(XmHTMLWidget html,XmHTMLImage * image,int xs,int ys)841 DrawFrame(XmHTMLWidget html, XmHTMLImage *image, int xs, int ys)
842 {
843 	int idx, width = 0, height = 0, fx, fy;
844 	GC gc = HTML_ATTR(gc);
845 	ToolkitAbstraction *tka = HTML_ATTR(tka);
846 
847 	_XmHTMLDebug(16, ("paint.c: DrawFrame, animation %s, frame %i, "
848 		"(x = %i, y = %i)\n", image->url, image->current_frame, xs, ys));
849 
850 	/* first reset the gc */
851 	RESET_GC(gc);
852 
853 	/*
854 	* First check if we are running this animation internally. If we aren't
855 	* we have a simple animation of which each frame has the same size and
856 	* no disposal method has been specified. This type of animations are blit
857 	* to screen directly.
858 	*/
859 	if(!ImageHasState(image))
860 	{
861 		/* index of current frame */
862 		idx = image->current_frame;
863 		width  = image->frames[idx].w;
864 		height = image->frames[idx].h;
865 
866 		/* can happen when a frame falls outside the logical screen area */
867 		if(image->frames[idx].pixmap != None)
868 		{
869 			/* plug in the clipmask */
870 			if(image->frames[idx].clip)
871 			{
872 				tka->SetClipOriginAndMask(tka, gc, xs, ys, image->frames[idx].clip);
873 			}
874 			/* blit frame to screen */
875 			tka->CopyArea(tka->dpy, image->frames[idx].pixmap, tka->win,
876 					gc, 0, 0, width, height, xs, ys);
877 		}
878 		/*
879 		* Jump to frame updating when we are not triggered
880 		* by an exposure, otherwise just return.
881 		*/
882 		if(ImageFrameRefresh(image))
883 			goto nextframe;
884 		return;
885 	}
886 	/*
887 	* If DrawFrame was triggered by an exposure, just blit current animation
888 	* state to screen and return immediatly.
889 	*/
890 	if(!ImageFrameRefresh(image))
891 	{
892 		tka->DrawImage(html, image, gc, 0, 0, image->width, image->height,
893 			xs, ys);
894 		return;
895 	}
896 
897 	/*****
898 	* If we get here we are running the animation internally. First check the
899 	* disposal method and update the current state accordingly *before*
900 	* putting the next frame on the display.
901 	* Pixmap can be None if a frame falls outside the logical screen area.
902 	* idx is the index of the previous frame (the frame that is currently
903 	* being displayed).
904 	*****/
905 	idx = image->current_frame ? image->current_frame - 1 : image->nframes - 1;
906 
907 	if(image->frames[idx].pixmap != None)
908 	{
909 		fx     = image->frames[idx].x;
910 		fy     = image->frames[idx].y;
911 		width  = image->frames[idx].w;
912 		height = image->frames[idx].h;
913 
914 		if(image->frames[idx].dispose == XmIMAGE_DISPOSE_BY_BACKGROUND)
915 		{
916 			_XmHTMLDebug(16, ("paint.c: DrawFrame, %s, disposing frame %i "
917 				"by background, x = %i, y = %i, %ix%i\n", image->url, idx,
918 				xs + fx, ys + fy, width, height));
919 
920 			/* we have a body image; get proper background tile offsets. */
921 			if(HTML_ATTR(body_image))
922 			{
923 				/* we have a body image, compute correct tile offsets */
924 				int tsx, tsy;
925 
926 				GET_TILE_OFFSETS(tsx,tsy);
927 
928 				_XmHTMLDebug(16, ("paint.c: DrawFrame: background disposal "
929 					"uses a tile with origin at (%i,%i)\n", tsx, tsy));
930 
931 				tka->SetFillStyle(tka->dpy, HTML_ATTR(bg_gc),
932 						tka->fill_style[GC_FILL_TILED]);
933 				tka->SetTile(tka->dpy, HTML_ATTR(bg_gc),
934 						HTML_ATTR(body_image)->pixmap);
935 				tka->SetTSOrigin(tka->dpy, HTML_ATTR(bg_gc),
936 						tsx - xs, tsy - ys);
937 
938 				/* paint it. */
939 				tka->FillRectangle(tka->dpy, image->pixmap, HTML_ATTR(bg_gc),
940 					fx, fy, width, height);
941 			}
942 			/*
943 			* No body image, do a fillrect in background color. clipmasks
944 			* are ignored since we are already restoring to background!
945 			*/
946 			else
947 			{
948 				tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_bg));
949 				tka->FillRectangle(tka->dpy, image->pixmap, gc, fx, fy, width,
950 						height);
951 				tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_fg));
952 			}
953 		}
954 		/*
955 		* no disposal method but we have a clipmask. Need to plug in
956 		* the background image or color. As we are completely overlaying
957 		* the current image with the new image, we can safely erase
958 		* the entire contents of the current state with the wanted
959 		* background, after which we use the clipmask to copy the requested
960 		* parts of the image on screen.
961 		*
962 		* Please note that this is *only* done for the very first frame
963 		* of such an animation. All other animations, whether they have a
964 		* clipmask or not, are put on top of this frame. Doing it for
965 		* other frames as well would lead to unwanted results as the
966 		* underlying portions of the animation would be replaced with the
967 		* current background, and thereby violating the none disposal
968 		* method logic.
969 		*/
970 		else if(image->frames[idx].dispose == XmIMAGE_DISPOSE_NONE &&
971 			idx == 0 && image->frames[idx].clip != None)
972 		{
973 			/* we have a body image, compute correct tile offsets */
974 			if(HTML_ATTR(body_image))
975 			{
976 				int tsx, tsy;
977 
978 				GET_TILE_OFFSETS(tsx,tsy);
979 
980 				_XmHTMLDebug(16, ("paint.c: DrawFrame: background disposal "
981 					"uses a tile with origin at (%i,%i)\n", tsx, tsy));
982 
983 				/* update gc values */
984 				tka->SetFillStyle(tka->dpy, HTML_ATTR(bg_gc),
985 						tka->fill_style[GC_FILL_TILED]);
986 				tka->SetTile(tka->dpy, HTML_ATTR(bg_gc),
987 						HTML_ATTR(body_image)->pixmap);
988 				tka->SetTSOrigin(tka->dpy, HTML_ATTR(bg_gc),
989 						tsx - xs, tsy - ys);
990 
991 				/* do a fillrect to render the background image */
992 				tka->FillRectangle(tka->dpy, image->pixmap, HTML_ATTR(bg_gc),
993 					fx, fy, width, height);
994 				/*
995 				* no need to reset the background gc, its only used for
996 				* overall background rendering.
997 				*/
998 			}
999 			else
1000 			{
1001 				/* do a plain fillrect in current background color */
1002 				tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_bg));
1003 				tka->FillRectangle(tka->dpy, image->pixmap, gc, fx, fy, width,
1004 						height);
1005 				tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_fg));
1006 			}
1007 
1008 			/* now plug in the clipmask */
1009 			tka->SetClipOriginAndMask(tka, gc, fx, fy, image->frames[idx].clip);
1010 
1011 			/* paint it. Use full image dimensions */
1012 			tka->CopyArea(tka->dpy, image->frames[idx].pixmap, image->pixmap,
1013 				gc, 0, 0, width, height, fx, fy);
1014 		}
1015 		/* dispose by previous (the only one to have a prev_state) */
1016 		else if(image->frames[idx].prev_state != None)
1017 		{
1018 			/* plug in the clipmask */
1019 			if(image->frames[idx].clip)
1020 			{
1021 				/* set gc values */
1022 				tka->SetClipOriginAndMask(tka, gc, fx, fy, image->frames[idx].clip);
1023 			}
1024 			/* put previous screen state on current state */
1025 			tka->CopyArea(tka->dpy, image->frames[idx].prev_state,
1026 					image->pixmap, gc, 0, 0, width, height, fx, fy);
1027 		}
1028 	}
1029 	/* reset gc */
1030 	RESET_GC(gc);
1031 
1032 	/* index of current frame */
1033 	idx = image->current_frame;
1034 
1035 	/* can happen when a frame falls outside the logical screen area */
1036 	if(image->frames[idx].pixmap != None)
1037 	{
1038 		fx      = image->frames[idx].x;
1039 		fy      = image->frames[idx].y;
1040 		width   = image->frames[idx].w;
1041 		height  = image->frames[idx].h;
1042 
1043 		/*
1044 		* get current screen state if we are to dispose of this frame by the
1045 		* previous state. The previous state is given by the current pixmap,
1046 		* so we just create a new pixmap and copy the current one into it.
1047 		* This is about the fastest method I can think of.
1048 		*/
1049 		if(image->frames[idx].dispose == XmIMAGE_DISPOSE_BY_PREVIOUS &&
1050 			image->frames[idx].prev_state == None)
1051 		{
1052 			Pixmap prev_state;
1053 			GC tmpGC;
1054 
1055 			/* create pixmap that is to receive the image */
1056 			prev_state = tka->CreatePixmap(tka->dpy, tka->win, width, height,
1057 				XCCGetDepth(HTML_ATTR(xcc)));
1058 
1059 			/* copy it */
1060 			tmpGC = tka->CreateGC(tka, prev_state, 0, 0);
1061 			tka->SetFunction(tka->dpy, tmpGC, tka->gc_func[GC_GXcopy]);
1062 			tka->CopyArea(tka->dpy, image->pixmap, prev_state, tmpGC,
1063 					fx, fy, width, height, 0, 0);
1064 
1065 			/* and save it */
1066 			image->frames[idx].prev_state = prev_state;
1067 
1068 			/* free and destroy */
1069 			tka->FreeGC(tka->dpy, tmpGC);
1070 		}
1071 		if(image->frames[idx].clip)
1072 		{
1073 			tka->SetClipOriginAndMask(tka, gc, fx, fy, image->frames[idx].clip);
1074 		}
1075 		tka->CopyArea(tka->dpy, image->frames[idx].pixmap, image->pixmap,
1076 				gc, 0, 0, width, height, fx, fy);
1077 
1078 		/* reset gc */
1079 		RESET_GC(gc);
1080 
1081 		/* blit current state to screen */
1082 		tka->CopyArea(tka->dpy, image->pixmap, tka->win, gc, 0, 0,
1083 			image->width, image->height, xs, ys);
1084 	}
1085 nextframe:
1086 	image->current_frame++;
1087 
1088 	/* will get set again by TimerCB */
1089 	image->options &= ~(IMG_FRAMEREFRESH);
1090 
1091 	if(image->current_frame == image->nframes)
1092 	{
1093 		image->current_frame = 0;
1094 		/*
1095 		* Sigh, when an animation is running forever (loop_count == 0) and
1096 		* some sucker is keeping XmHTML up forever, chances are that we *can*
1097 		* exceed INT_MAX. Since some systems don't wrap their integers properly
1098 		* when their value exceeds INT_MAX, we can't keep increasing the
1099 		* current loop count forever since this *can* lead to a crash (which
1100 		* is a potential security hole). To prevent this from happening, we
1101 		* *only* increase current_loop when run this animation a limited
1102 		* number of times.
1103 		*/
1104 		if(image->loop_count)
1105 		{
1106 			image->current_loop++;
1107 			/*
1108 			* If the current loop count matches the total loop count, depromote
1109 			* the animation to a regular image so the next time the timer
1110 			* callback is activated we will enter normal image processing.
1111 			*/
1112 			if(image->current_loop == image->loop_count)
1113 				image->options &= ~(IMG_ISANIM);
1114 		}
1115 	}
1116 	/*
1117 	* To prevent racing conditions, we must first remove an existing
1118 	* timeout proc before adding a new one.
1119 	*/
1120 	REMOVE_TIMEOUTPROC(image);
1121 
1122 	image->proc_id = tka->AddTimeOut(image->context,
1123 		image->frames[idx].timeout, TimerCB, image);
1124 
1125 	_XmHTMLDebug(16, ("paint.c: DrawFrame end\n"));
1126 }
1127 
1128 /*****
1129 * Name: 		DrawRule
1130 * Return Type: 	void
1131 * Description: 	paints a horizontal rule.
1132 * In:
1133 *	html:		XmHTMLWidget id;
1134 *	data:		element data;
1135 * Returns:
1136 *	nothing.
1137 * Note:
1138 *	Rules that had their noshade attribute set are identiefied by having
1139 *	a non-zero y_offset field in the data. We support a color attribute
1140 *	in this case as well, so colored rules are possible. They are also
1141 *	possible if a hr is in the proper context: one of the extensions
1142 *	supported by XmHTML is a color attribute on the DIV tag.
1143 *****/
1144 static void
DrawRule(XmHTMLWidget html,XmHTMLObjectTableElement data)1145 DrawRule(XmHTMLWidget html, XmHTMLObjectTableElement data)
1146 {
1147 	int dy;
1148 	GC gc;
1149 	int xs, ys;
1150 	ToolkitAbstraction *tka = HTML_ATTR(tka);
1151 
1152 	/*
1153 	* recompute rule layout if we are auto-sizing.
1154 	* Needs to be done since the formatted_width is only known once
1155 	* the entire document has been laid out.
1156 	*/
1157 	if(HTML_ATTR(resize_width))
1158 	{
1159 		int x;
1160 		int width = HTML_ATTR(work_width) - HTML_ATTR(margin_width);
1161 
1162 		/* horizontal offset */
1163 		x = HTML_ATTR(margin_width) + data->ident;
1164 
1165 		/* See if we have an width specification */
1166 		if(data->len != 0)
1167 		{
1168 			if(data->len < 0)	/* % spec */
1169 				width *= (float)(-1*data->len/100.);
1170 			else	/* pixel spec, cut if wider than available */
1171 				width = (data->len > width ? width : data->len);
1172 			/* alignment is only honored if there is a width spec */
1173 			switch(data->halign)
1174 			{
1175 				case XmHALIGN_RIGHT:
1176 					x = HTML_ATTR(margin_width) + HTML_ATTR(work_width) - width;
1177 					break;
1178 				case XmHALIGN_CENTER:
1179 					x = HTML_ATTR(margin_width) +
1180 					(HTML_ATTR(work_width) - width - HTML_ATTR(margin_width))/2;
1181 				default:	/* shutup compiler */
1182 					break;
1183 			}
1184 		}
1185 		/* Save updated position and width */
1186 		data->x = x;
1187 		data->width = width;
1188 	}
1189 
1190 	xs = data->x - HTML_ATTR(scroll_x);
1191 
1192 	/* vertical offset */
1193 	dy = 0;
1194 	ys = data->y - HTML_ATTR(scroll_y);
1195 
1196 	if(data->height)
1197 	{
1198 		if(data->y_offset)	/* noshade */
1199 		{
1200 			gc = HTML_ATTR(gc);
1201 			tka->SetLineAttributes(tka->dpy, gc, 1,
1202 				tka->line_style[GC_LINE_SOLID],
1203 				tka->cap_style[GC_CAP_BUTT],
1204 				tka->join_style[GC_JOIN_BEVEL]);
1205 			tka->SetForeground(tka->dpy, gc, data->fg);
1206 			tka->FillRectangle(tka->dpy, tka->win, gc, xs, ys + dy,
1207 					data->width, data->height);
1208 		}
1209 		else
1210 		{
1211 			if(data->fg != HTML_ATTR(body_fg))
1212 				XmHTMLTkaRecomputeShadowColors(html, data->fg);
1213 
1214 			tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1215 				MGR_ATTR(bottom_shadow_GC), xs, ys + dy, data->width,
1216 				data->height, 1, XmSHADOW_IN);
1217 
1218 			if(data->fg != HTML_ATTR(body_fg))
1219 				XmHTMLTkaRecomputeShadowColors(html, HTML_ATTR(body_bg));
1220 		}
1221 	}
1222 	else
1223 	{
1224 		if(data->y_offset) /* noshade */
1225 		{
1226 			gc = HTML_ATTR(gc);
1227 			tka->SetLineAttributes(tka->dpy, gc, 1,
1228 				tka->line_style[GC_LINE_SOLID],
1229 				tka->cap_style[GC_CAP_BUTT],
1230 				tka->join_style[GC_JOIN_BEVEL]);
1231 			tka->SetForeground(tka->dpy, gc, data->fg);
1232 			tka->DrawLine(tka->dpy, tka->win, gc, xs, ys + dy,
1233 					xs + data->width, ys + dy);
1234 			tka->DrawLine(tka->dpy, tka->win, gc, xs, ys + dy + 1,
1235 					xs + data->width, ys + dy + 1);
1236 		}
1237 		else
1238 		{
1239 			if(data->fg != HTML_ATTR(body_fg))
1240 				XmHTMLTkaRecomputeShadowColors(html, data->fg);
1241 
1242 			tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1243 				MGR_ATTR(bottom_shadow_GC), xs, ys + dy, data->width, 2,
1244 				1, XmSHADOW_IN);
1245 
1246 			if(data->fg != HTML_ATTR(body_fg))
1247 				XmHTMLTkaRecomputeShadowColors(html, HTML_ATTR(body_bg));
1248 		}
1249 	}
1250 }
1251 
1252 static void
DrawBullet(XmHTMLWidget html,XmHTMLObjectTableElement data)1253 DrawBullet(XmHTMLWidget html, XmHTMLObjectTableElement data)
1254 {
1255 	GC gc = HTML_ATTR(gc);
1256 	int ys, xs;
1257 	ToolkitAbstraction *tka = HTML_ATTR(tka);
1258 
1259 	/* reset colors, an anchor might have been drawn before */
1260 	tka->SetForeground(tka->dpy, gc, data->fg);
1261 	tka->SetLineAttributes(tka->dpy, gc, 1,
1262 			tka->line_style[GC_LINE_SOLID],
1263 			tka->cap_style[GC_CAP_BUTT],
1264 			tka->join_style[GC_JOIN_BEVEL]);
1265 
1266 	xs = data->x - HTML_ATTR(scroll_x);
1267 	ys = data->y - HTML_ATTR(scroll_y);
1268 
1269 	switch(data->marker)
1270 	{
1271 		case XmMARKER_DISC:
1272 			tka->FillArc(tka->dpy, tka->win, gc,
1273 				xs - 2*data->width, ys - data->height,
1274 				data->width, data->width, 0, 23040);
1275 			break;
1276 		case XmMARKER_SQUARE:
1277 			tka->DrawRectangle(tka->dpy, tka->win, gc,
1278 				xs - 2*data->width, ys - data->height,
1279 				data->width, data->width);
1280 			break;
1281 		case XmMARKER_CIRCLE:
1282 			tka->DrawArc(tka->dpy, tka->win, gc,
1283 				xs - 2*data->width, ys - data->height,
1284 				data->width, data->width, 0, 23040);
1285 			break;
1286 		default:
1287 			/* Textual markers are right-aligned. */
1288 			tka->DrawString(tka, HTML_ATTR(default_font), gc,
1289 				xs - data->width, ys, data->text, data->len);
1290 			break;
1291 	}
1292 }
1293 
1294 /*****
1295 * Name:			DrawAnchorButton
1296 * Return Type: 	void
1297 * Description: 	render an anchor as a button.
1298 * In:
1299 *	tka:		paint functions
1300 *	x,y:		position of top-left origin;
1301 *	width:		width of button;
1302 *	height:		height of buttom;
1303 *	top_sha..:  top shadow color to use;
1304 *	bottom_..:	bottom shadow color to use;
1305 * Returns:
1306 *
1307 *****/
1308 static void
DrawAnchorButton(ToolkitAbstraction * tka,int x,int y,Dimension width,Dimension height,GC top_shadow_GC,GC bottom_shadow_GC)1309 DrawAnchorButton(ToolkitAbstraction *tka, int x, int y, Dimension width,
1310 	Dimension height, GC top_shadow_GC, GC bottom_shadow_GC)
1311 {
1312 	tka->DrawShadows(tka->dpy, tka->win, top_shadow_GC, bottom_shadow_GC,
1313 		x, y, width, height, 1, XmSHADOW_OUT);
1314 }
1315 
1316 /*****
1317 * Name:			DrawTable
1318 * Return Type: 	XmHTMLObjectTableElement
1319 * Description: 	render a table.
1320 * In:
1321 *	html:		XmHTMLWidget id;
1322 *	start:		starting object;
1323 *	data_end:	ending object
1324 * Returns:
1325 *	last element of the table or NULL.
1326 *****/
1327 static XmHTMLObjectTableElement
DrawTable(XmHTMLWidget html,XmHTMLObjectTableElement start,XmHTMLObjectTableElement data_end,Bool inNestedTable)1328 DrawTable(XmHTMLWidget html, XmHTMLObjectTableElement start,
1329 	XmHTMLObjectTableElement data_end, Bool inNestedTable)
1330 {
1331 	XmHTMLTable *table;
1332 	TableRow *row = NULL;
1333 	TableCell *cell = NULL;
1334 	int nrows, i, j;
1335 
1336 	/* pick up table data */
1337 	table = start->table;
1338 
1339 	_XmHTMLDebug(16, ("paint.c: DrawTable, table_start: x = %i, "
1340 		"y = %i\n", start->x, start->y));
1341 
1342 	if(table == NULL)
1343 		return(start);
1344 
1345 	/*****
1346 	* The first table in a stack of tables contains all data for all
1347 	* table childs it contains. The first table child is the master
1348 	* table itself. So when a table doesn't have a child table it *is*
1349 	* a child table itself and thus we should add the left offset
1350 	* to the initial horizontal position.
1351 	*****/
1352 	if(table->childs)
1353 		table = &(table->childs[0]);
1354 
1355 #if 0
1356 	/* only render table border if we have to */
1357 	if(table->props->framing != TFRAME_VOID)
1358 		DrawTableBorder(html, table);
1359 #endif
1360 	nrows = table->nrows;
1361 
1362 	for(i = 0; i < nrows; i++)
1363 	{
1364 		row = &(table->rows[i]);
1365 
1366 		for(j = 0; j < row->ncells; j++)
1367 		{
1368 			cell = &(row->cells[j]);
1369 
1370 			/* only draw something if it falls in the exposure area */
1371 			if((HTML_ATTR(paint_y) > cell->owner->y + cell->owner->height ||
1372 				HTML_ATTR(paint_height) < cell->owner->y) ||
1373 				(HTML_ATTR(paint_x) > cell->owner->x + cell->owner->width ||
1374 				HTML_ATTR(paint_width) < cell->owner->x))
1375 				continue;
1376 
1377 			/*****
1378 			* Render the cell if it has a background color/image or when
1379 			* we have to draw borders.
1380 			*****/
1381 			/* check if we have to draw a border */
1382 			if(HTML_ATTR(body_image) ||
1383 				cell->props->bg != HTML_ATTR(body_bg) ||
1384 				cell->props->bg_image != NULL ||
1385 				cell->props->ruling != TRULE_NONE)
1386 				DrawCellFrame(html, cell);
1387 
1388 			DrawCellContent(html, cell->start, cell->end, inNestedTable);
1389 		}
1390 	}
1391 
1392 	/* only render table border if we have to */
1393 	if(table->props->framing != TFRAME_VOID)
1394 		DrawTableBorder(html, table);
1395 
1396 	/* sanity */
1397 	if(table->end && data_end)
1398 		return(table->end->y < data_end->y ? table->end->prev : data_end->prev);
1399 	return(table->end ? table->end->prev : data_end);
1400 }
1401 
1402 /*****
1403 * Name:			DrawCellContent
1404 * Return Type: 	void
1405 * Description: 	render the contents of a single cell
1406 * In:
1407 *	html:		XmHTMLWidget id;
1408 *	start:		starting object;
1409 *	end:		ending object
1410 * Returns:
1411 *	nothing.
1412 *****/
1413 static void
DrawCellContent(XmHTMLWidget html,XmHTMLObjectTableElement start,XmHTMLObjectTableElement end,Bool inNestedTable)1414 DrawCellContent(XmHTMLWidget html, XmHTMLObjectTableElement start,
1415 	XmHTMLObjectTableElement end, Bool inNestedTable)
1416 {
1417 	XmHTMLObjectTableElement temp;
1418 
1419 	temp = start;
1420 
1421 	_XmHTMLDebug(16, ("paint.c: DrawCellContent, table_start: x = %i, "
1422 		"y = %i\n", start->x, start->y));
1423 
1424 	while(temp && temp != end)
1425 	{
1426 		_XmHTMLDebug(16, ("paint.c: DrawCellContent, checking object %s\n",
1427 			temp->object ? temp->object->element : "<dummy element>"));
1428 
1429 		switch(temp->object_type)
1430 		{
1431 			case OBJ_TEXT:
1432 			case OBJ_PRE_TEXT:
1433 				/*
1434 				* First check if this is an image. DrawImage will render
1435 				* an image as an anchor if required.
1436 				*/
1437 				if(temp->text_data & TEXT_IMAGE)
1438 					_XmHTMLDrawImage(html, temp, 0, False);
1439 				else
1440 				{
1441 					/* form scrolling gets handled by formScroll in XmHTML.c */
1442 					if(temp->text_data & TEXT_FORM)
1443 						break;
1444 					else
1445 					{
1446 						if(temp->text_data & TEXT_ANCHOR)
1447 							DrawAnchor(html, temp);
1448 						else if ( !inNestedTable )  /* text in nested tables is already drawn when processing the master table. Anti-aliased fonts look ugly when drawn multiple times, therefore we avoid that with that if-condition. */
1449 							DrawText(html, temp);
1450 					}
1451 				}
1452 				break;
1453 			case OBJ_BULLET:
1454 				DrawBullet(html, temp);
1455 				break;
1456 			case OBJ_HRULE:
1457 				DrawRule(html, temp);
1458 				break;
1459 			case OBJ_TABLE:
1460 				/* nested tables, hehehe */
1461 				(void)DrawTable(html, temp, end, /* inNestedTable = */ True);
1462 				break;
1463 			case OBJ_TABLE_FRAME:
1464 			case OBJ_IMG:
1465 			case OBJ_APPLET:
1466 			case OBJ_BLOCK:
1467 			case OBJ_NONE:
1468 				_XmHTMLDebug(16, ("paint.c: DrawTable, skipping undrawable "
1469 					"object %s\n",
1470 					temp->object ? temp->object->element : "<dummy element>"));
1471 				break;
1472 			default:
1473 				_XmHTMLWarning(__WFUNC__(html, "DrawTable"), XMHTML_MSG_78);
1474 		}
1475 		temp = temp->next;
1476 	}
1477 	_XmHTMLDebug(16, ("paint.c: DrawCellContent End\n"));
1478 }
1479 
1480 /*****
1481 * Name:			DrawCellFrame
1482 * Return Type: 	void
1483 * Description: 	renders a frame around a table cell. Optionally fills it
1484 *				with a background color or image.
1485 * In:
1486 *	html:		XmHTMLWidget id;
1487 *	cell:		cell to be framed;
1488 * Returns:
1489 *	nothing.
1490 *****/
1491 static void
DrawCellFrame(XmHTMLWidget html,TableCell * cell)1492 DrawCellFrame(XmHTMLWidget html, TableCell *cell)
1493 {
1494 	XmHTMLObjectTableElement data = cell->owner;
1495 	int xs, ys, width, height;
1496 	int rx, ry, rw, rh;		/* cell background region coordinates */
1497 	Byte rule = DRAW_BOX;
1498 	ToolkitAbstraction *tka = HTML_ATTR(tka);
1499 
1500 	/* which sides do we have to render? */
1501 	switch(cell->props->ruling)
1502 	{
1503 		case TRULE_NONE:	/* no rules, only bg color/image will be done */
1504 			rule = DRAW_NONE;
1505 			break;
1506 		case TRULE_GROUPS:	/* only horizontal rules */
1507 		case TRULE_ROWS:	/* only horizontal rules */
1508 			rule = DRAW_LEFT | DRAW_RIGHT;
1509 			break;
1510 		case TRULE_COLS:	/* only vertical rules */
1511 			rule = DRAW_TOP | DRAW_BOTTOM;
1512 			break;
1513 		case TRULE_ALL:		/* all rules */
1514 			break;
1515 	}
1516 
1517 	/* width & height of the box to be drawn */
1518 	rw   = width  = data->width;
1519 	rh   = height = data->height;
1520 
1521 	/*****
1522 	* Initial, absolute, positions.
1523 	* Painting uses upper-left coordinates.
1524 	******/
1525 	rx = xs = data->x;
1526 	ry = ys = data->y;
1527 
1528 	/*****
1529 	* Correct absolute positions for updating only the exposed region of
1530 	* a cell's *background*
1531 	* Not doing this would repaint the background of an entire cell while
1532 	* it's contents are only redrawn for the exposed region.
1533 	*****/
1534 	if(rx < HTML_ATTR(paint_x))
1535 	{
1536 		/* origin too far left */
1537 		rw -= (HTML_ATTR(paint_x) - rx);
1538 		rx = HTML_ATTR(paint_x);
1539 	}
1540 
1541 	if(rx + rw > HTML_ATTR(paint_width))
1542 	{
1543 		/* width too far right */
1544 		rw = HTML_ATTR(paint_width) - rx;
1545 	}
1546 
1547 	if(ry < HTML_ATTR(paint_y))
1548 	{
1549 		/* origin too high */
1550 		rh -= (HTML_ATTR(paint_y) - ry);
1551 		ry = HTML_ATTR(paint_y);
1552 	}
1553 
1554 	if(ry + rh > HTML_ATTR(paint_height))
1555 	{
1556 		/* height too low */
1557 		rh = HTML_ATTR(paint_height) - ry;
1558 	}
1559 
1560 	if(rh <= 0 || rw <= 0)
1561 		return;
1562 
1563 	/*****
1564 	* Translate absolute coordinates to relative ones by substracting
1565 	* the region that has been scrolled.
1566 	*****/
1567 	xs -= HTML_ATTR(scroll_x);
1568 	ys -= HTML_ATTR(scroll_y);
1569 	rx -= HTML_ATTR(scroll_x);
1570 	ry -= HTML_ATTR(scroll_y);
1571 
1572 	/* Do we have a unique background color? */
1573 	if(HTML_ATTR(body_image) || cell->owner->bg != HTML_ATTR(body_bg))
1574 	{
1575 		tka->SetForeground(tka->dpy, HTML_ATTR(gc), data->bg);
1576 		tka->FillRectangle(tka->dpy, tka->win, HTML_ATTR(gc), rx, ry, rw, rh);
1577 	}
1578 
1579 	/* Do we have a background image? */
1580 	if(cell->props->bg_image != NULL)
1581 	{
1582 		int tile_width, tile_height, x_dist, y_dist;
1583 		int ntiles_x, ntiles_y, tsx, tsy;
1584 		int x_offset, y_offset;
1585 		XmHTMLImage *bg_image = cell->props->bg_image;
1586 
1587 		tile_width  = bg_image->width;
1588 		tile_height = bg_image->height;
1589 
1590 		/* total distance covered so far */
1591 		x_dist = HTML_ATTR(scroll_x) + rx;
1592 		y_dist = HTML_ATTR(scroll_y) + ry;
1593 
1594 		/* no of horizontal tiles */
1595 		ntiles_x = (int)(x_dist/tile_width);
1596 		ntiles_y = (int)(y_dist/tile_height);
1597 		x_offset = x_dist - (ntiles_x * tile_width);
1598 		y_offset = y_dist - (ntiles_y * tile_height);
1599 		tsx = rx - x_offset;
1600 		tsy = ry - y_offset;
1601 
1602 		/* set gc values */
1603 		tka->SetFillStyle(tka->dpy, HTML_ATTR(bg_gc),
1604 				tka->fill_style[GC_FILL_TILED]);
1605 		tka->SetTile(tka->dpy, HTML_ATTR(bg_gc), bg_image->pixmap);
1606 		tka->SetTSOrigin(tka->dpy, HTML_ATTR(bg_gc),
1607 				tsx - rx, tsy - ry);
1608 
1609 		/* paint it. */
1610 		tka->FillRectangle(tka->dpy, tka->win, HTML_ATTR(bg_gc), rx, ry,
1611 				rw, rh);
1612 	}
1613 
1614 	/* no border drawing if we don't have to */
1615 	if(cell->props->border == 0)
1616 		return;
1617 
1618 	/* draw an entire box and return */
1619 	if(DRAW_BOX == (rule & DRAW_BOX))
1620 	{
1621 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1622 			MGR_ATTR(bottom_shadow_GC), xs, ys - 1, width, height,
1623 			1, XmSHADOW_IN);
1624 		return;
1625 	}
1626 
1627 	/* top border */
1628 	if(rule & DRAW_TOP)
1629 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1630 			MGR_ATTR(bottom_shadow_GC), xs, ys - 1, width, 2,
1631 			1, XmSHADOW_OUT);
1632 
1633 	/* left border */
1634 	if(rule & DRAW_LEFT)
1635 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1636 			MGR_ATTR(bottom_shadow_GC), xs, ys, 2, height,
1637 			1, XmSHADOW_OUT);
1638 
1639 	/* bottom border */
1640 	if(rule & DRAW_BOTTOM)
1641 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1642 			MGR_ATTR(bottom_shadow_GC), xs, ys + height - 1, width, 2,
1643 			1, XmSHADOW_OUT);
1644 
1645 	/* right border */
1646 	if(rule & DRAW_RIGHT)
1647 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1648 			MGR_ATTR(bottom_shadow_GC), xs + width, ys, 2, height,
1649 			1, XmSHADOW_OUT);
1650 }
1651 
1652 /*****
1653 * Name:			DrawTableBorder
1654 * Return Type: 	void
1655 * Description: 	renders a frame around a table.
1656 * In:
1657 *	html:		XmHTMLWidget id;
1658 *	table:		table for which a frame must be rendered;
1659 * Returns:
1660 *	nothing.
1661 *****/
1662 static void
DrawTableBorder(XmHTMLWidget html,XmHTMLTable * table)1663 DrawTableBorder(XmHTMLWidget html, XmHTMLTable *table)
1664 {
1665 	int xs, ys;
1666 	XmHTMLObjectTableElement data = table->owner;
1667 	int bwidth, width, height;
1668 	Byte rule = DRAW_BOX;
1669 	ToolkitAbstraction *tka = HTML_ATTR(tka);
1670 
1671 	bwidth = table->props->border;
1672 	width  = data->width;
1673 	height = data->height;
1674 
1675 	/* horizontal offset, taking scrolling into account */
1676 	xs = data->x - HTML_ATTR(scroll_x);
1677 
1678 	/* vertical offset, taking scrolling into account */
1679 	ys = data->y - HTML_ATTR(scroll_y);
1680 
1681 	_XmHTMLDebug(16, ("Drawing table border: x = %i, y = %i, width = %i, "
1682 		"height = %i\n", xs, ys, width, height));
1683 
1684 	/* which sides do we have to render? */
1685 	switch(table->props->framing)
1686 	{
1687 		case TFRAME_VOID:
1688 			return;
1689 			break;
1690 		case TFRAME_ABOVE:
1691 			rule = DRAW_TOP;
1692 			break;
1693 		case TFRAME_BELOW:
1694 			rule = DRAW_BOTTOM;
1695 			break;
1696 		case TFRAME_LEFT:
1697 			rule = DRAW_LEFT;
1698 			break;
1699 		case TFRAME_RIGHT:
1700 			rule = DRAW_RIGHT;
1701 			break;
1702 		case TFRAME_HSIDES:
1703 			rule = DRAW_LEFT|DRAW_RIGHT;
1704 			break;
1705 		case TFRAME_VSIDES:
1706 			rule = DRAW_TOP|DRAW_BOTTOM;
1707 			break;
1708 		case TFRAME_BOX:
1709 		case TFRAME_BORDER:
1710 			break;
1711 	}
1712 
1713 	/* draw an entire box and return */
1714 	if(DRAW_BOX == (rule & DRAW_BOX))
1715 	{
1716 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1717 			MGR_ATTR(bottom_shadow_GC), xs, ys - 1, width + 1, height + 1,
1718 			bwidth, XmSHADOW_OUT);
1719 		return;
1720 	}
1721 
1722 	/* top border */
1723 	if(rule & DRAW_TOP)
1724 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1725 			MGR_ATTR(bottom_shadow_GC), xs, ys, width, table->vmargin,
1726 			bwidth, XmSHADOW_OUT);
1727 
1728 	/* left border */
1729 	if(rule & DRAW_LEFT)
1730 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1731 			MGR_ATTR(bottom_shadow_GC), xs, ys, table->hmargin, height,
1732 			bwidth, XmSHADOW_OUT);
1733 
1734 	/* bottom border */
1735 	if(rule & DRAW_BOTTOM)
1736 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1737 			MGR_ATTR(bottom_shadow_GC), xs, ys + height, width, table->vmargin,
1738 			bwidth, XmSHADOW_IN);
1739 
1740 	/* right border */
1741 	if(rule & DRAW_RIGHT)
1742 		tka->DrawShadows(tka->dpy, tka->win, MGR_ATTR(top_shadow_GC),
1743 			MGR_ATTR(bottom_shadow_GC), xs + width, ys, table->hmargin, height,
1744 			bwidth, XmSHADOW_IN);
1745 }
1746 
1747 /********
1748 **** Private exported paint functions
1749 ********/
1750 
1751 /*****
1752 * Name: 		_XmHTMLPaint
1753 * Return Type: 	void
1754 * Description: 	re-paints the given amount of data.
1755 * In:
1756 *	w:			widget to paint text to
1757 *	start:		start item
1758 *	end:		end item
1759 * Returns:
1760 *	nothing.
1761 *****/
1762 void
_XmHTMLPaint(XmHTMLWidget html,XmHTMLObjectTable * start,XmHTMLObjectTable * end)1763 _XmHTMLPaint(XmHTMLWidget html, XmHTMLObjectTable *start,
1764 	XmHTMLObjectTable *end)
1765 {
1766 	XmHTMLObjectTableElement temp;
1767 
1768 	_XmHTMLDebug(16, ("paint.c: _XmHTMLPaint Start, paint_start: x = %i, "
1769 		"y = %i, paint_end: x = %i, y = %i\n", start->x, start->y,
1770 		 end ? end->x : -1, end ? end->y : -1));
1771 
1772 	temp = start;
1773 
1774 	while(temp && temp != end)
1775 	{
1776 		_XmHTMLDebug(16, ("paint.c: _XmHTMLPaint, painting object %p with name \"%s\" with type %i, and pos (%i, %i)\n",
1777 			(void*)temp, temp->object ? temp->object->element : "<dummy element>", temp->object_type, temp->x, temp->y));
1778 
1779 		switch(temp->object_type)
1780 		{
1781 			case OBJ_TEXT:
1782 			case OBJ_PRE_TEXT:
1783 				/*
1784 				* First check if this is an image. DrawImage will render
1785 				* an image as an anchor if required.
1786 				*/
1787 				if(temp->text_data & TEXT_IMAGE)
1788 					_XmHTMLDrawImage(html, temp, 0, False);
1789 				else
1790 				{
1791 					/* form scrolling gets handled by formScroll in XmHTML.c */
1792 					if(temp->text_data & TEXT_FORM)
1793 						break;
1794 					else
1795 					{
1796 						if(temp->text_data & TEXT_ANCHOR)
1797 							DrawAnchor(html, temp);
1798 						else
1799 							DrawText(html, temp);
1800 					}
1801 				}
1802 				break;
1803 			case OBJ_BULLET:
1804 				DrawBullet(html, temp);
1805 				break;
1806 			case OBJ_HRULE:
1807 				DrawRule(html, temp);
1808 				break;
1809 			case OBJ_IMG:
1810 				_XmHTMLWarning(__WFUNC__(html, "_XmHTMLPaint"), XMHTML_MSG_89);
1811 				break;
1812 			case OBJ_TABLE:
1813 			case OBJ_TABLE_FRAME:
1814 				temp = DrawTable(html, temp, end, /* inNestedTable = */ False);
1815 				break;
1816 			case OBJ_APPLET:
1817 			case OBJ_BLOCK:
1818 			case OBJ_NONE:
1819 				break;
1820 			default:
1821 				_XmHTMLWarning(__WFUNC__(html, "_XmHTMLPaint"), XMHTML_MSG_78);
1822 		}
1823 
1824 		_XmHTMLDebug(16, ("paint.c: after processing object: temp = %p\n", temp ));
1825 
1826 		if ( temp && temp != end ) {
1827 			temp = temp->next;
1828 		}
1829 	}
1830 	_XmHTMLDebug(16, ("paint.c: _XmHTMLPaint End\n"));
1831 	return;
1832 }
1833 
1834 /*****
1835 * Name: 		_XmHTMLRestartAnimations
1836 * Return Type: 	void
1837 * Description: 	restarts all animations. Called by SetValues when the
1838 *				value of the XmNfreezeAnimations resource switches from
1839 *				True to False.
1840 * In:
1841 *	html:		XmHTMLWidget id
1842 * Returns:
1843 *	nothing.
1844 *****/
1845 void
_XmHTMLRestartAnimations(XmHTMLWidget html)1846 _XmHTMLRestartAnimations(XmHTMLWidget html)
1847 {
1848 	XmHTMLImage *tmp;
1849 
1850 	for(tmp = HTML_ATTR(images); tmp != NULL; tmp = tmp->next)
1851 	{
1852 		if(ImageIsAnim(tmp))
1853 		{
1854 			tmp->options |= IMG_FRAMEREFRESH;
1855 			_XmHTMLDrawImage(html, tmp->owner, 0, False);
1856 		}
1857 	}
1858 }
1859 
1860 /*****
1861 * Name: 		_XmHTMLDrawImage
1862 * Return Type: 	void
1863 * Description: 	image refresher.
1864 * In:
1865 *	w:			XmHTMLWidget id
1866 *	data:		Object data.
1867 *	y_offset:	vertical offset for screen copying;
1868 *	from_timerCB: true when called from the timeout proc.
1869 * Returns:
1870 *	nothing
1871 * Note:
1872 *	this is a funny routine: it does plain images as well as
1873 *	animations. Animations with a loop count of zero will loop
1874 *	forever. Other animations will loop their counts and when that
1875 *	has been reached they are depromoted to regular images.
1876 *	The only way to restore animations with a loop count to animations
1877 *	again is to reload them (XmHTMLImageUpdate).
1878 *****/
1879 void
_XmHTMLDrawImage(XmHTMLWidget html,XmHTMLObjectTableElement data,int y_offset,Boolean from_timerCB)1880 _XmHTMLDrawImage(XmHTMLWidget html, XmHTMLObjectTableElement data, int y_offset,
1881 	Boolean from_timerCB)
1882 {
1883 	int xs, ys;
1884 	XmHTMLImage *image;
1885 	GC gc;
1886 	ToolkitAbstraction *tka = HTML_ATTR(tka);
1887 
1888 	/* sanity check */
1889 	if((image = data->words->image) == NULL)
1890 		return;
1891 
1892 	gc = (ImageIsProgressive(image) ? HTML_ATTR(plc_gc) : HTML_ATTR(gc));
1893 
1894 	/* compute correct image offsets */
1895 	xs = data->words->x - HTML_ATTR(scroll_x);
1896 	ys = data->words->y - HTML_ATTR(scroll_y);
1897 
1898 	_XmHTMLDebug(16, ("paint.c: DrawImage start, x = %i, y = %i\n",
1899 		data->x, data->y));
1900 
1901 	/*
1902 	* animation frames should be repainted if the animation is somewhere
1903 	* in the visible area.
1904 	*/
1905 	if(ImageFrameRefresh(image))
1906 	{
1907 		if(xs + image->width < 0 || xs > HTML_ATTR(work_width) ||
1908 			ys + image->height < 0 || ys > HTML_ATTR(work_height))
1909 		{
1910 			_XmHTMLDebug(16, ("paint.c: DrawImage end, animation %s not in "
1911 				"visible area.\n", image->url));
1912 			REMOVE_TIMEOUTPROC(image);
1913 			return;
1914 		}
1915 	}
1916 	/*
1917 	* Only do this when we are repainting this image as a result of an
1918 	* exposure.
1919 	*/
1920 	if(!from_timerCB)
1921 	{
1922 		/* anchors are always repainted if they are visible */
1923 		/* fix 03/25/97-01, kdh */
1924 		if(data->text_data & TEXT_ANCHOR)
1925 		{
1926 			if(xs + image->width > 0 && xs < HTML_ATTR(work_width) &&
1927 				ys + image->height > 0 && ys < HTML_ATTR(work_height))
1928 				DrawImageAnchor(html, data);
1929 		}
1930 		else
1931 		{
1932 			/*
1933 			* When any of the two cases below is true, the image is not in the
1934 			* exposed screen area. Not doing this check would cause a visible
1935 			* flicker of the screen when scrolling: the entire image would be
1936 			* repainted even if it is not visible.
1937 			*/
1938 			if(HTML_ATTR(paint_y) > data->words->y + image->height ||
1939 				HTML_ATTR(paint_height) < data->words->y)
1940 			{
1941 				_XmHTMLDebug(16, ("paint.c: DrawImage end, out of vertical "
1942 					"range.\n"));
1943 				return;
1944 			}
1945 			if(HTML_ATTR(paint_x) > data->words->x + image->width ||
1946 				HTML_ATTR(paint_width) < data->words->x)
1947 			{
1948 				_XmHTMLDebug(16, ("paint.c: DrawImage end, out of horizontal "
1949 					"range.\n"));
1950 				return;
1951 			}
1952 		}
1953 	}
1954 
1955 	/*
1956 	* If this is an animation, paint next frame or restore current
1957 	* state when we are scrolling this animation on and off screen.
1958 	*/
1959 	if(ImageIsAnim(image))
1960 		DrawFrame(html, image, xs, ys);
1961 	else if(image->pixmap != None)
1962 	{
1963 		/* put in clipmask */
1964 		if(image->clip)
1965 		{
1966 			/* set gc values */
1967 			tka->SetClipOriginAndMask(tka, gc, xs, ys, image->clip);
1968 		}
1969 		/* copy to screen */
1970 		tka->DrawImage(html, image, gc, 0, y_offset, image->width,
1971 			image->height, xs, ys + y_offset);
1972 	}
1973 	/* reset gc */
1974 	RESET_GC(gc);
1975 
1976 	/*****
1977 	* Paint the alt text if images are disabled or when this image is
1978 	* delayed.
1979 	*****/
1980 	if((!HTML_ATTR(images_enabled) ||
1981 		(image->html_image && ImageInfoDelayed(image->html_image))) &&
1982 		!(data->text_data & TEXT_ANCHOR))
1983 	{
1984 		tka->SetForeground(tka->dpy, gc, HTML_ATTR(body_fg));
1985 
1986 		/* put text inside bounding rectangle */
1987 		xs += image->width + 4;
1988 		ys += image->height/2 + 4;
1989 		tka->DrawString(tka, data->words->font, gc, xs, ys,
1990 			data->words->word, data->words->len);
1991 	}
1992 
1993 	/* check if we have to draw the imagemap bounding boxes */
1994 	if(image->map_type == XmMAP_CLIENT && HTML_ATTR(imagemap_draw))
1995 		_XmHTMLDrawImagemapSelection(html, image);
1996 
1997 	_XmHTMLDebug(16, ("paint.c: DrawImage end\n"));
1998 }
1999 
2000 /*****
2001 * Name: 		_XmHTMLPaintAnchorUnselected
2002 * Return Type: 	void
2003 * Description:  paints the currently active anchor in an unactivated state.
2004 *				_XmHTMLPaint will do the proper rendering.
2005 * In:
2006 *	w:			HTML widget of which to unset the current anchor
2007 * Returns:
2008 *	nothing.
2009 *****/
2010 void
_XmHTMLPaintAnchorUnSelected(XmHTMLWidget html)2011 _XmHTMLPaintAnchorUnSelected(XmHTMLWidget html)
2012 {
2013 	XmHTMLObjectTable *start, *end;
2014 
2015 	start = HTML_ATTR(current_anchor);
2016 
2017 	/* pick up the anchor end. An anchor ends when the raw worddata changes. */
2018 	for(end = start; end != NULL && end->object == start->object;
2019 		end = end->next)
2020 		end->anchor_state = ANCHOR_UNSELECTED;
2021 
2022 	_XmHTMLFullDebug(1, ("XmHTML.c: _XmHTMLPaintAnchorUnselected, unselecting "
2023 			"anchor: %s\n", start->anchor->href));
2024 
2025 	/* paint it... */
2026 	_XmHTMLPaint(html, start, end);
2027 
2028 	/* ...and invalidate current selection */
2029 	HTML_ATTR(current_anchor) = NULL;
2030 }
2031 
2032 /*****
2033 * Name: 		_XmHTMLPaintAnchorSelected
2034 * Return Type: 	void
2035 * Description:  paints the current active in an activated state.
2036 *				_XmHTMLPaint will do the proper rendering.
2037 * In:
2038 *	html:		HTML widget of which to set the current anchor
2039 * Returns:
2040 *	nothing.
2041 *****/
2042 void
_XmHTMLPaintAnchorSelected(XmHTMLWidget html,XmHTMLWord * anchor)2043 _XmHTMLPaintAnchorSelected(XmHTMLWidget html, XmHTMLWord *anchor)
2044 {
2045 	XmHTMLObjectTable *start, *end;
2046 
2047 	/* save as the current active anchor */
2048 	start = HTML_ATTR(current_anchor) = anchor->owner;
2049 
2050 	/* pick up anchor end. */
2051 	for(end = start; end != NULL && end->object == start->object;
2052 		end = end->next)
2053 		end->anchor_state = ANCHOR_SELECTED;
2054 
2055 	_XmHTMLFullDebug(1, ("XmHTML.c: _XmHTMLPaintAnchorSelected, selecting "
2056 		"anchor: %s\n", start->anchor->href));
2057 
2058 	/* paint it */
2059 	_XmHTMLPaint(html, start, end);
2060 }
2061 
2062 /*****
2063 * Name: 		_XmHTMLPaintAnchorLeave
2064 * Return Type: 	void
2065 * Description:  remove anchor highlighting.
2066 * In:
2067 *	html:		HTML widget of which to set the current anchor
2068 * Returns:
2069 *	nothing.
2070 *****/
2071 void
_XmHTMLPaintAnchorLeave(XmHTMLWidget html)2072 _XmHTMLPaintAnchorLeave(XmHTMLWidget html)
2073 {
2074 	XmHTMLObjectTable *start, *end;
2075 
2076 	/* save as the current active anchor */
2077 	start = HTML_ATTR(armed_anchor);
2078 
2079 	/* pick up the anchor end. */
2080 	for(end = start; end != NULL && end->object == start->object;
2081 		end = end->next)
2082 		end->anchor_state = ANCHOR_UNSELECTED;
2083 
2084 	_XmHTMLFullDebug(1, ("XmHTML.c: _XmHTMLPaintAnchorLeave, leaving "
2085 		"anchor: %s\n", start->anchor->href));
2086 
2087 	/* paint it */
2088 	_XmHTMLPaint(html, start, end);
2089 
2090 	HTML_ATTR(armed_anchor) = NULL;
2091 }
2092 
2093 /*****
2094 * Name: 		_XmHTMLPaintAnchorEntry
2095 * Return Type: 	void
2096 * Description: 	paints a highlight on the given anchor.
2097 * In:
2098 *	html:		XmHTMLWidget id;
2099 *	anchor:		anchor to receive highlighting.
2100 * Returns:
2101 *	nothing.
2102 *****/
2103 void
_XmHTMLPaintAnchorEntry(XmHTMLWidget html,XmHTMLObjectTable * anchor)2104 _XmHTMLPaintAnchorEntry(XmHTMLWidget html, XmHTMLObjectTable *anchor)
2105 {
2106 	XmHTMLObjectTable *start, *end;
2107 
2108 	/* save as the current active anchor */
2109 	start = HTML_ATTR(armed_anchor) = anchor;
2110 
2111 	/* pick up anchor end */
2112 	for(end = start; end != NULL && end->object == start->object;
2113 		end = end->next)
2114 		end->anchor_state = ANCHOR_INSELECT;
2115 
2116 	_XmHTMLFullDebug(1, ("XmHTML.c: _XmHTMLPaintAnchorEntry, entering "
2117 		"anchor: %s\n", start->anchor->href));
2118 
2119 	/* paint it */
2120 	_XmHTMLPaint(html, start, end);
2121 }
2122 
2123