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