1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*  This file is part of the GtkHTML library.
3  *
4  *  Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *  Copyright (C) 1997 Torben Weis (weis@kde.org)
6  *  Copyright (C) 1999, 2000 Helix Code, Inc.
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Library General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  *  Library General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Library General Public License
19  *  along with this library; see the file COPYING.LIB.  If not, write to
20  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  *  Boston, MA 02110-1301, USA.
22 */
23 
24 #include <config.h>
25 #include <string.h>
26 
27 #include "htmlclue.h"
28 #include "htmlclueflow.h"
29 #include "htmlcluealigned.h"
30 #include "htmlcluev.h"
31 #include "htmlcolor.h"
32 #include "htmlcolorset.h"
33 #include "htmlcursor.h"
34 #include "htmlengine.h"
35 #include "htmlengine-edit.h"
36 #include "htmlengine-edit-cut-and-paste.h"
37 #include "htmlengine-save.h"
38 #include "htmlframe.h"
39 #include "htmlinterval.h"
40 #include "htmlobject.h"
41 #include "htmlpainter.h"
42 #include "htmltable.h"
43 #include "htmltablecell.h"
44 #include "htmltext.h"
45 #include "htmlrule.h"
46 #include "htmltype.h"
47 #include "htmlsettings.h"
48 
49 #include "gtkhtmldebug.h"
50 
51 
52 HTMLObjectClass html_object_class;
53 
54 #define HO_CLASS(x) HTML_OBJECT_CLASS (HTML_OBJECT (x)->klass)
55 
56 static void remove_child (HTMLObject *self, HTMLObject *child) G_GNUC_NORETURN;
57 
58 static void
destroy(HTMLObject * self)59 destroy (HTMLObject *self)
60 {
61 #define GTKHTML_MEM_DEBUG 1
62 #if GTKHTML_MEM_DEBUG
63 	self->parent = HTML_OBJECT (0xdeadbeef);
64 	self->next = HTML_OBJECT (0xdeadbeef);
65 	self->prev = HTML_OBJECT (0xdeadbeef);
66 #else
67 	self->next = NULL;
68 	self->prev = NULL;
69 #endif
70 
71 	g_datalist_clear (&self->object_data);
72 	g_datalist_clear (&self->object_data_nocp);
73 
74 	g_free (self->id);
75 	self->id = NULL;
76 
77 	if (self->redraw_pending) {
78 		self->free_pending = TRUE;
79 	} else {
80 		g_free (self);
81 	}
82 }
83 
84 static void
copy(HTMLObject * self,HTMLObject * dest)85 copy (HTMLObject *self,
86       HTMLObject *dest)
87 {
88 	dest->klass = self->klass;
89 	dest->parent = NULL;
90 	dest->prev = NULL;
91 	dest->next = NULL;
92 	dest->x = 0;
93 	dest->y = 0;
94 	dest->ascent = self->ascent;
95 	dest->descent = self->descent;
96 	dest->width = self->width;
97 	dest->min_width = self->min_width;
98 	dest->max_width = self->max_width;
99 	dest->pref_width = self->pref_width;
100 	dest->percent = self->percent;
101 	dest->flags = self->flags;
102 	dest->redraw_pending = FALSE;
103 	dest->selected = FALSE;
104 	dest->free_pending = FALSE;
105 	dest->change = self->change;
106 	dest->draw_focused = FALSE;
107 	dest->id = g_strdup (self->id);
108 
109 	g_datalist_init (&dest->object_data);
110 	html_object_copy_data_from_object (dest, self);
111 
112 	g_datalist_init (&dest->object_data_nocp);
113 }
114 
115 static HTMLObject *
op_copy(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)116 op_copy (HTMLObject *self,
117          HTMLObject *parent,
118          HTMLEngine *e,
119          GList *from,
120          GList *to,
121          guint *len)
122 {
123 	if ((!from || GPOINTER_TO_INT (from->data) == 0)
124 	    && (!to || GPOINTER_TO_INT (to->data) == html_object_get_length (self))) {
125 		*len += html_object_get_recursive_length (self);
126 
127 		return html_object_dup (self);
128 	} else
129 		return html_engine_new_text_empty (e);
130 }
131 
132 static HTMLObject *
op_cut(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)133 op_cut (HTMLObject *self,
134         HTMLEngine *e,
135         GList *from,
136         GList *to,
137         GList *left,
138         GList *right,
139         guint *len)
140 {
141 	if ((!from || GPOINTER_TO_INT (from->data) == 0)
142 	    && (!to || GPOINTER_TO_INT (to->data) == html_object_get_length (self))) {
143 		if (!html_object_could_remove_whole (self, from, to, left, right)) {
144 			HTMLObject *empty = html_engine_new_text_empty (e);
145 
146 			if (e->cursor->object == self)
147 				e->cursor->object = empty;
148 			html_clue_append_after (HTML_CLUE (self->parent), empty, self);
149 			html_object_change_set (empty, HTML_CHANGE_ALL_CALC);
150 			html_object_check_cut_lists (self, empty, left, right);
151 		} else
152 			html_object_move_cursor_before_remove (self, e);
153 
154 		html_object_change_set (self, HTML_CHANGE_ALL_CALC);
155 		html_object_change_set (self->parent, HTML_CHANGE_ALL_CALC);
156 		/* force parent redraw */
157 		self->parent->width = 0;
158 		html_object_remove_child (self->parent, self);
159 		*len += html_object_get_recursive_length (self);
160 
161 		return self;
162 	} else
163 		return html_engine_new_text_empty (e);
164 }
165 
166 static gboolean
merge(HTMLObject * self,HTMLObject * with,HTMLEngine * e,GList ** left,GList ** right,HTMLCursor * cursor)167 merge (HTMLObject *self,
168        HTMLObject *with,
169        HTMLEngine *e,
170        GList **left,
171        GList **right,
172        HTMLCursor *cursor)
173 {
174 	if (self->parent) {
175 		html_object_change_set (self->parent, HTML_CHANGE_ALL_CALC);
176 		self->parent->width = 0;
177 	}
178 
179 	return FALSE;
180 }
181 
182 static void
remove_child(HTMLObject * self,HTMLObject * child)183 remove_child (HTMLObject *self,
184               HTMLObject *child)
185 {
186 	g_warning ("REMOVE CHILD unimplemented for ");
187 	gtk_html_debug_dump_object_type (self);
188 	g_assert_not_reached ();
189 }
190 
191 static void
split(HTMLObject * self,HTMLEngine * e,HTMLObject * child,gint offset,gint level,GList ** left,GList ** right)192 split (HTMLObject *self,
193        HTMLEngine *e,
194        HTMLObject *child,
195        gint offset,
196        gint level,
197        GList **left,
198        GList **right)
199 {
200 	if (child || (offset && html_object_get_length (self) != offset)) {
201 		g_warning ("don't know how to SPLIT ");
202 		gtk_html_debug_dump_object_type (self);
203 		return;
204 	}
205 
206 	if (offset) {
207 		if (!self->next) {
208 			html_clue_append (HTML_CLUE (self->parent), html_engine_new_text_empty (e));
209 		}
210 		*left  = g_list_prepend (*left,  self);
211 		*right = g_list_prepend (*right, self->next);
212 	} else {
213 		if (!self->prev) {
214 			e->cursor->object = html_engine_new_text_empty (e);
215 			e->cursor->offset = 0;
216 			html_clue_prepend (HTML_CLUE (self->parent), e->cursor->object);
217 		}
218 		*left  = g_list_prepend (*left,  self->prev);
219 		*right = g_list_prepend (*right, self);
220 	}
221 	level--;
222 
223 	if (level && self->parent)
224 		html_object_split (self->parent, e, offset ? self->next : self, 0, level, left, right);
225 }
226 
227 static void
draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)228 draw (HTMLObject *o,
229       HTMLPainter *p,
230       gint x,
231       gint y,
232       gint width,
233       gint height,
234       gint tx,
235       gint ty)
236 {
237 	/* Do nothing by default.  We don't know how to paint ourselves.  */
238 }
239 
240 static gboolean
is_transparent(HTMLObject * self)241 is_transparent (HTMLObject *self)
242 {
243 	return TRUE;
244 }
245 
246 static HTMLFitType
fit_line(HTMLObject * o,HTMLPainter * painter,gboolean start_of_line,gboolean first_run,gboolean next_to_floating,gint width_left)247 fit_line (HTMLObject *o,
248           HTMLPainter *painter,
249           gboolean start_of_line,
250           gboolean first_run,
251           gboolean next_to_floating,
252           gint width_left)
253 {
254 	return (o->width <= width_left || (first_run && !next_to_floating)) ? HTML_FIT_COMPLETE : HTML_FIT_NONE;
255 }
256 
257 static gboolean
html_object_real_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)258 html_object_real_calc_size (HTMLObject *o,
259                             HTMLPainter *painter,
260                             GList **changed_objs)
261 {
262 	return FALSE;
263 }
264 
265 static gint
calc_min_width(HTMLObject * o,HTMLPainter * painter)266 calc_min_width (HTMLObject *o,
267                 HTMLPainter *painter)
268 {
269 	html_object_calc_size (o, painter, NULL);
270 	return o->width;
271 }
272 
273 static gint
calc_preferred_width(HTMLObject * o,HTMLPainter * painter)274 calc_preferred_width (HTMLObject *o,
275                       HTMLPainter *painter)
276 {
277 	html_object_calc_size (o, painter, NULL);
278 	return o->width;
279 }
280 
281 static void
set_max_width(HTMLObject * o,HTMLPainter * painter,gint max_width)282 set_max_width (HTMLObject *o,
283                HTMLPainter *painter,
284                gint max_width)
285 {
286 	o->max_width = max_width;
287 }
288 
289 static void
set_max_height(HTMLObject * o,HTMLPainter * painter,gint max_height)290 set_max_height (HTMLObject *o,
291                 HTMLPainter *painter,
292                 gint max_height)
293 {
294 }
295 
296 static gint
get_left_margin(HTMLObject * self,HTMLPainter * painter,gint y,gboolean with_aligned)297 get_left_margin (HTMLObject *self,
298                  HTMLPainter *painter,
299                  gint y,
300                  gboolean with_aligned)
301 {
302 	return 0;
303 }
304 
305 static gint
get_right_margin(HTMLObject * self,HTMLPainter * painter,gint y,gboolean with_aligned)306 get_right_margin (HTMLObject *self,
307                   HTMLPainter *painter,
308                   gint y,
309                   gboolean with_aligned)
310 {
311 	return MAX (self->max_width, self->width);
312 }
313 
314 static void
set_painter(HTMLObject * o,HTMLPainter * painter)315 set_painter (HTMLObject *o,
316              HTMLPainter *painter)
317 {
318 }
319 
320 static void
reset(HTMLObject * o)321 reset (HTMLObject *o)
322 {
323 	/* o->width = 0;
324 	 * o->ascent = 0;
325 	 * o->descent = 0; */
326 }
327 
328 static const gchar *
get_url(HTMLObject * o,gint offset)329 get_url (HTMLObject *o,
330          gint offset)
331 {
332 	return NULL;
333 }
334 
335 static const gchar *
get_target(HTMLObject * o,gint offset)336 get_target (HTMLObject *o,
337             gint offset)
338 {
339 	return NULL;
340 }
341 
342 static const gchar *
get_src(HTMLObject * o)343 get_src (HTMLObject *o)
344 {
345 	return NULL;
346 }
347 
348 static HTMLAnchor *
find_anchor(HTMLObject * o,const gchar * name,gint * x,gint * y)349 find_anchor (HTMLObject *o,
350              const gchar *name,
351              gint *x,
352              gint *y)
353 {
354 	return NULL;
355 }
356 
357 static void
set_bg_color(HTMLObject * o,GdkColor * color)358 set_bg_color (HTMLObject *o,
359               GdkColor *color)
360 {
361 }
362 
363 static GdkColor *
get_bg_color(HTMLObject * o,HTMLPainter * p)364 get_bg_color (HTMLObject *o,
365               HTMLPainter *p)
366 {
367 	if (o->parent)
368 		return html_object_get_bg_color (o->parent, p);
369 
370 	if (p->widget && GTK_IS_HTML (p->widget)) {
371 		HTMLEngine *e = html_object_engine (o, GTK_HTML (p->widget)->engine);
372 		return &((html_colorset_get_color (e->settings->color_set, HTMLBgColor))->color);
373 	}
374 
375 	return NULL;
376 }
377 
378 static HTMLObject *
check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)379 check_point (HTMLObject *self,
380              HTMLPainter *painter,
381              gint x,
382              gint y,
383              guint *offset_return,
384              gboolean for_cursor)
385 {
386 	if (x >= self->x
387 	    && x < self->x + self->width
388 	    && y >= self->y - self->ascent
389 	    && y < self->y + self->descent) {
390 		if (offset_return != NULL)
391 			*offset_return = 0;
392 		return self;
393 	}
394 
395 	return NULL;
396 }
397 
398 static gboolean
relayout(HTMLObject * self,HTMLEngine * engine,HTMLObject * child)399 relayout (HTMLObject *self,
400           HTMLEngine *engine,
401           HTMLObject *child)
402 {
403 	/* FIXME gint types of this stuff might change in `htmlobject.h',
404 	 * remember to sync.  */
405 	guint prev_width;
406 	guint prev_ascent, prev_descent;
407 	gboolean changed;
408 
409 	if (html_engine_frozen (engine))
410 		return FALSE;
411 
412 	prev_width = self->width;
413 	prev_ascent = self->ascent;
414 	prev_descent = self->descent;
415 
416 	/* Notice that this will reset ascent and descent which we
417 	 * need afterwards.  Yeah, yuck, bleargh.  */
418 	html_object_reset (self);
419 
420 	/* Crappy hack to make crappy htmlclueflow.c happy.  */
421 	if (self->y < self->ascent + self->descent) {
422 		g_warning ("htmlobject.c:relayout -- Eeek! This should not happen!  "
423 			   "Y value < height of object!\n");
424 		self->y = 0;
425 	} else {
426 		self->y -= prev_ascent + prev_descent;
427 	}
428 
429 	changed = html_object_calc_size (self, engine->painter, NULL);
430 
431 	if (prev_width == self->width
432 	    && prev_ascent == self->ascent
433 	    && prev_descent == self->descent) {
434 		gtk_html_debug_log (engine->widget,
435 				    "relayout: %s %p did not change.\n",
436 				    html_type_name (HTML_OBJECT_TYPE (self)),
437 				    self);
438 		if (changed)
439 			html_engine_queue_draw (engine, self);
440 
441 		return FALSE;
442 	}
443 
444 	gtk_html_debug_log (engine->widget, "relayout: %s %p changed.\n",
445 			    html_type_name (HTML_OBJECT_TYPE (self)), self);
446 
447 	if (self->parent == NULL) {
448 		/* FIXME resize the widget, e.g. scrollbars and such.  */
449 		html_engine_queue_draw (engine, self);
450 
451 		/* FIXME extreme ugliness.  */
452 		self->x = 0;
453 		self->y = self->ascent;
454 	} else {
455 		/* Relayout our parent starting from us.  */
456 		if (!html_object_relayout (self->parent, engine, self))
457 			html_engine_queue_draw (engine, self);
458 	}
459 
460 	/* If the object has shrunk, we have to clean the areas around
461 	 * it so that we don't leave garbage on the screen.  FIXME:
462 	 * this wastes some time if there is an object on the right of
463 	 * or under this one.  */
464 
465 	if (prev_ascent + prev_descent > self->ascent + self->descent)
466 		html_engine_queue_clear (engine,
467 					 self->x,
468 					 self->y + self->descent,
469 					 self->width,
470 					 (prev_ascent + prev_descent
471 					  - (self->ascent + self->descent)));
472 
473 	if (prev_width > self->width)
474 		html_engine_queue_clear (engine,
475 					 self->x + self->width,
476 					 self->y - self->ascent,
477 					 prev_width - self->width,
478 					 self->ascent + self->descent);
479 
480 	return TRUE;
481 }
482 
483 static HTMLVAlignType
get_valign(HTMLObject * self)484 get_valign (HTMLObject *self)
485 {
486 	return HTML_VALIGN_BOTTOM;
487 }
488 
489 static gboolean
accepts_cursor(HTMLObject * self)490 accepts_cursor (HTMLObject *self)
491 {
492 	return FALSE;
493 }
494 
495 static void
get_cursor(HTMLObject * self,HTMLPainter * painter,guint offset,gint * x1,gint * y1,gint * x2,gint * y2)496 get_cursor (HTMLObject *self,
497             HTMLPainter *painter,
498             guint offset,
499             gint *x1,
500             gint *y1,
501             gint *x2,
502             gint *y2)
503 {
504 	html_object_get_cursor_base (self, painter, offset, x2, y2);
505 
506 	*x1 = *x2;
507 	*y1 = *y2 - self->ascent;
508 	*y2 += self->descent - 1;
509 }
510 
511 static void
get_cursor_base(HTMLObject * self,HTMLPainter * painter,guint offset,gint * x,gint * y)512 get_cursor_base (HTMLObject *self,
513                  HTMLPainter *painter,
514                  guint offset,
515                  gint *x,
516                  gint *y)
517 {
518 	html_object_calc_abs_position (self, x, y);
519 
520 	if (offset > 0)
521 		*x += self->width;
522 }
523 
524 static guint
get_length(HTMLObject * self)525 get_length (HTMLObject *self)
526 {
527 	return html_object_accepts_cursor (self) ? 1 : 0;
528 }
529 
530 static guint
get_line_length(HTMLObject * self,HTMLPainter * p,gint line_offset)531 get_line_length (HTMLObject *self,
532                  HTMLPainter *p,
533                  gint line_offset)
534 {
535 	return html_object_get_length (self);
536 }
537 
538 static guint
get_recursive_length(HTMLObject * self)539 get_recursive_length (HTMLObject *self)
540 {
541 	return html_object_get_length (self);
542 }
543 
544 static gboolean
select_range(HTMLObject * self,HTMLEngine * engine,guint start,gint length,gboolean queue_draw)545 select_range (HTMLObject *self,
546               HTMLEngine *engine,
547               guint start,
548               gint length,
549               gboolean queue_draw)
550 {
551 	gboolean selected;
552 	gboolean changed;
553 
554 	selected = length > 0 || (length == -1 && start < html_object_get_length (self)) || html_object_is_container (self) ? TRUE : FALSE;
555 	changed  = (!selected && self->selected) || (selected && !self->selected) ? TRUE : FALSE;
556 
557 	self->selected = selected;
558 
559 	return changed;
560 }
561 
562 static void
append_selection_string(HTMLObject * self,GString * buffer)563 append_selection_string (HTMLObject *self,
564                          GString *buffer)
565 {
566 }
567 
568 static void
forall(HTMLObject * self,HTMLEngine * e,HTMLObjectForallFunc func,gpointer data)569 forall (HTMLObject *self,
570         HTMLEngine *e,
571         HTMLObjectForallFunc func,
572         gpointer data)
573 {
574 	(* func) (self, e, data);
575 }
576 
577 static HTMLEngine *
get_engine(HTMLObject * self,HTMLEngine * e)578 get_engine (HTMLObject *self,
579             HTMLEngine *e)
580 {
581 	return e;
582 }
583 
584 static gboolean
is_container(HTMLObject * self)585 is_container (HTMLObject *self)
586 {
587 	return FALSE;
588 }
589 
590 static gboolean
save(HTMLObject * self,HTMLEngineSaveState * state)591 save (HTMLObject *self,
592       HTMLEngineSaveState *state)
593 {
594 	return TRUE;
595 }
596 
597 static gboolean
save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)598 save_plain (HTMLObject *self,
599             HTMLEngineSaveState *state,
600             gint requested_width)
601 {
602 	return TRUE;
603 }
604 
605 static gint
check_page_split(HTMLObject * self,HTMLPainter * p,gint y)606 check_page_split (HTMLObject *self,
607                   HTMLPainter *p,
608                   gint y)
609 {
610 	return 0;
611 }
612 
613 static gboolean
search(HTMLObject * self,HTMLSearch * info)614 search (HTMLObject *self,
615         HTMLSearch *info)
616 {
617 	/* not found by default */
618 	return FALSE;
619 }
620 
621 static HTMLObject *
next(HTMLObject * self,HTMLObject * child)622 next (HTMLObject *self,
623       HTMLObject *child)
624 {
625 	return child->next;
626 }
627 
628 static HTMLObject *
prev(HTMLObject * self,HTMLObject * child)629 prev (HTMLObject *self,
630       HTMLObject *child)
631 {
632 	return child->prev;
633 }
634 
635 static HTMLObject *
head(HTMLObject * self)636 head (HTMLObject *self)
637 {
638 	return NULL;
639 }
640 
641 static HTMLObject *
tail(HTMLObject * self)642 tail (HTMLObject *self)
643 {
644 	return NULL;
645 }
646 
647 static HTMLClearType
get_clear(HTMLObject * self)648 get_clear (HTMLObject *self)
649 {
650 	return HTML_CLEAR_NONE;
651 }
652 
653 static HTMLDirection
html_object_real_get_direction(HTMLObject * o)654 html_object_real_get_direction (HTMLObject *o)
655 {
656 	if (o->parent)
657 		return html_object_get_direction (o->parent);
658 
659 	return HTML_DIRECTION_DERIVED;
660 }
661 
662 static gboolean
html_object_real_cursor_forward(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)663 html_object_real_cursor_forward (HTMLObject *self,
664                                  HTMLCursor *cursor,
665                                  HTMLEngine *engine)
666 {
667 	gint len;
668 
669 	g_assert (self);
670 	g_assert (cursor->object == self);
671 
672 	if (html_object_is_container (self))
673 		return FALSE;
674 
675 	len = html_object_get_length (self);
676 	if (cursor->offset < len) {
677 		cursor->offset++;
678 		cursor->position++;
679 		return TRUE;
680 	} else
681 		return FALSE;
682 }
683 
684 static gboolean
html_cursor_allow_zero_offset(HTMLCursor * cursor,HTMLObject * o)685 html_cursor_allow_zero_offset (HTMLCursor *cursor,
686                                HTMLObject *o)
687 {
688 	if (cursor->offset == 1) {
689 		HTMLObject *prev;
690 
691 		prev = html_object_prev_not_slave (o);
692 		if (!prev || HTML_IS_CLUEALIGNED (prev))
693 			return TRUE;
694 		else {
695 			while (prev && !html_object_accepts_cursor (prev))
696 				prev = html_object_prev_not_slave (prev);
697 
698 			if (!prev)
699 				return TRUE;
700 		}
701 	}
702 
703 	return FALSE;
704 }
705 
706 static gboolean
html_object_real_cursor_backward(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)707 html_object_real_cursor_backward (HTMLObject *self,
708                                   HTMLCursor *cursor,
709                                   HTMLEngine *engine)
710 {
711 	g_assert (self);
712 	g_assert (cursor->object == self);
713 
714 	if (html_object_is_container (self))
715 		return FALSE;
716 
717 	if (cursor->offset > 1 || html_cursor_allow_zero_offset (cursor, self)) {
718 		cursor->offset--;
719 		cursor->position--;
720 		return TRUE;
721 	}
722 
723 	return FALSE;
724 }
725 
726 static gboolean
html_object_real_cursor_right(HTMLObject * self,HTMLPainter * painter,HTMLCursor * cursor)727 html_object_real_cursor_right (HTMLObject *self,
728                                HTMLPainter *painter,
729                                HTMLCursor *cursor)
730 {
731 	HTMLDirection dir = html_object_get_direction (self);
732 
733 	g_assert (self);
734 	g_assert (cursor->object == self);
735 
736 	if (html_object_is_container (self))
737 		return FALSE;
738 
739 	if (dir != HTML_DIRECTION_RTL) {
740 		gint len;
741 
742 		len = html_object_get_length (self);
743 
744 		if (cursor->offset < len) {
745 			cursor->offset++;
746 			cursor->position++;
747 			return TRUE;
748 		}
749 	} else {
750 		if (cursor->offset > 1 || html_cursor_allow_zero_offset (cursor, self)) {
751 			cursor->offset--;
752 			cursor->position--;
753 			return TRUE;
754 		}
755 	}
756 
757 	return FALSE;
758 }
759 
760 static gboolean
html_object_real_cursor_left(HTMLObject * self,HTMLPainter * painter,HTMLCursor * cursor)761 html_object_real_cursor_left (HTMLObject *self,
762                               HTMLPainter *painter,
763                               HTMLCursor *cursor)
764 {
765 	HTMLDirection dir = html_object_get_direction (self);
766 
767 	g_assert (self);
768 	g_assert (cursor->object == self);
769 
770 	if (html_object_is_container (self))
771 		return FALSE;
772 
773 	if (dir != HTML_DIRECTION_RTL) {
774 		if (cursor->offset > 1 || html_cursor_allow_zero_offset (cursor, self)) {
775 			cursor->offset--;
776 			cursor->position--;
777 			return TRUE;
778 		}
779 	} else {
780 		gint len;
781 
782 		len = html_object_get_length (self);
783 
784 		if (cursor->offset < len) {
785 			cursor->offset++;
786 			cursor->position++;
787 			return TRUE;
788 		}
789 	}
790 
791 	return FALSE;
792 }
793 
794 static gboolean
html_object_real_backspace(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)795 html_object_real_backspace (HTMLObject *self,
796                             HTMLCursor *cursor,
797                             HTMLEngine *engine)
798 {
799 	html_cursor_backward (cursor, engine);
800 	html_engine_delete (engine);
801 
802 	return TRUE;
803 }
804 
805 static gint
html_object_real_get_right_edge_offset(HTMLObject * o,HTMLPainter * painter,gint offset)806 html_object_real_get_right_edge_offset (HTMLObject *o,
807                                         HTMLPainter *painter,
808                                         gint offset)
809 {
810 	return html_object_get_length (o);
811 }
812 
813 static gint
html_object_real_get_left_edge_offset(HTMLObject * o,HTMLPainter * painter,gint offset)814 html_object_real_get_left_edge_offset (HTMLObject *o,
815                                        HTMLPainter *painter,
816                                        gint offset)
817 {
818 	return 0;
819 }
820 
821 /* Class initialization.  */
822 
823 void
html_object_type_init(void)824 html_object_type_init (void)
825 {
826 	html_object_class_init (&html_object_class, HTML_TYPE_OBJECT, sizeof (HTMLObject));
827 }
828 
829 void
html_object_class_init(HTMLObjectClass * klass,HTMLType type,guint object_size)830 html_object_class_init (HTMLObjectClass *klass,
831                         HTMLType type,
832                         guint object_size)
833 {
834 	g_return_if_fail (klass != NULL);
835 
836 	/* Set type.  */
837 	klass->type = type;
838 	klass->object_size = object_size;
839 
840 	/* Install virtual methods.  */
841 	klass->destroy = destroy;
842 	klass->copy = copy;
843 	klass->op_copy = op_copy;
844 	klass->op_cut = op_cut;
845 	klass->merge = merge;
846 	klass->remove_child = remove_child;
847 	klass->split = split;
848 	klass->draw = draw;
849 	klass->is_transparent = is_transparent;
850 	klass->fit_line = fit_line;
851 	klass->calc_size = html_object_real_calc_size;
852 	klass->set_max_width = set_max_width;
853 	klass->set_max_height = set_max_height;
854 	klass->get_left_margin = get_left_margin;
855 	klass->get_right_margin = get_right_margin;
856 	klass->set_painter = set_painter;
857 	klass->reset = reset;
858 	klass->calc_min_width = calc_min_width;
859 	klass->calc_preferred_width = calc_preferred_width;
860 	klass->get_url = get_url;
861 	klass->get_target = get_target;
862 	klass->get_src = get_src;
863 	klass->find_anchor = find_anchor;
864 	klass->set_link = NULL;
865 	klass->set_bg_color = set_bg_color;
866 	klass->get_bg_color = get_bg_color;
867 	klass->check_point = check_point;
868 	klass->relayout = relayout;
869 	klass->get_valign = get_valign;
870 	klass->accepts_cursor = accepts_cursor;
871 	klass->get_cursor = get_cursor;
872 	klass->get_cursor_base = get_cursor_base;
873 	klass->select_range = select_range;
874 	klass->append_selection_string = append_selection_string;
875 	klass->forall = forall;
876 	klass->is_container = is_container;
877 	klass->save = save;
878 	klass->save_plain = save_plain;
879 	klass->check_page_split = check_page_split;
880 	klass->search = search;
881 	klass->search_next = search;
882 	klass->get_length = get_length;
883 	klass->get_line_length = get_line_length;
884 	klass->get_recursive_length = get_recursive_length;
885 	klass->next = next;
886 	klass->prev = prev;
887 	klass->head = head;
888 	klass->tail = tail;
889 	klass->get_engine = get_engine;
890 	klass->get_clear = get_clear;
891 	klass->get_direction = html_object_real_get_direction;
892 	klass->cursor_forward = html_object_real_cursor_forward;
893 	klass->cursor_forward_one = html_object_real_cursor_forward;
894 	klass->cursor_backward = html_object_real_cursor_backward;
895 	klass->cursor_backward_one = html_object_real_cursor_backward;
896 	klass->cursor_left = html_object_real_cursor_left;
897 	klass->cursor_right = html_object_real_cursor_right;
898 	klass->backspace = html_object_real_backspace;
899 	klass->get_right_edge_offset = html_object_real_get_right_edge_offset;
900 	klass->get_left_edge_offset = html_object_real_get_left_edge_offset;
901 }
902 
903 void
html_object_init(HTMLObject * o,HTMLObjectClass * klass)904 html_object_init (HTMLObject *o,
905                   HTMLObjectClass *klass)
906 {
907 	o->klass = klass;
908 
909 	o->parent = NULL;
910 	o->prev = NULL;
911 	o->next = NULL;
912 
913 	/* we don't have any info cached in the beginning */
914 	o->change = HTML_CHANGE_ALL;
915 
916 	o->x = 0;
917 	o->y = 0;
918 
919 	o->ascent = 0;
920 	o->descent = 0;
921 
922 	o->width = 0;
923 	o->max_width = 0;
924 	o->min_width = 0;
925 	o->pref_width = 0;
926 	o->percent = 0;
927 
928 	o->flags = HTML_OBJECT_FLAG_FIXEDWIDTH; /* FIXME Why? */
929 
930 	o->redraw_pending = FALSE;
931 	o->free_pending = FALSE;
932 	o->selected = FALSE;
933 	o->draw_focused = FALSE;
934 
935 	g_datalist_init (&o->object_data);
936 	g_datalist_init (&o->object_data_nocp);
937 
938 	o->id = NULL;
939 }
940 
941 HTMLObject *
html_object_new(HTMLObject * parent)942 html_object_new (HTMLObject *parent)
943 {
944 	HTMLObject *o;
945 
946 	o = g_new0 (HTMLObject, 1);
947 	html_object_init (o, &html_object_class);
948 
949 	return o;
950 }
951 
952 
953 /* Object duplication.  */
954 
955 HTMLObject *
html_object_dup(HTMLObject * object)956 html_object_dup (HTMLObject *object)
957 {
958 	HTMLObject *new;
959 
960 	g_return_val_if_fail (object != NULL, NULL);
961 
962 	new = g_malloc (object->klass->object_size);
963 	html_object_copy (object, new);
964 
965 	return new;
966 }
967 
968 HTMLObject *
html_object_op_copy(HTMLObject * self,HTMLObject * parent,HTMLEngine * e,GList * from,GList * to,guint * len)969 html_object_op_copy (HTMLObject *self,
970                      HTMLObject *parent,
971                      HTMLEngine *e,
972                      GList *from,
973                      GList *to,
974                      guint *len)
975 {
976 	return (* HO_CLASS (self)->op_copy) (self, parent, e, from, to, len);
977 }
978 
979 HTMLObject *
html_object_op_cut(HTMLObject * self,HTMLEngine * e,GList * from,GList * to,GList * left,GList * right,guint * len)980 html_object_op_cut (HTMLObject *self,
981                     HTMLEngine *e,
982                     GList *from,
983                     GList *to,
984                     GList *left,
985                     GList *right,
986                     guint *len)
987 {
988 	return (* HO_CLASS (self)->op_cut) (self, e, from, to, left, right, len);
989 }
990 
991 gboolean
html_object_merge(HTMLObject * self,HTMLObject * with,HTMLEngine * e,GList ** left,GList ** right,HTMLCursor * cursor)992 html_object_merge (HTMLObject *self,
993                    HTMLObject *with,
994                    HTMLEngine *e,
995                    GList **left,
996                    GList **right,
997                    HTMLCursor *cursor)
998 {
999 	if ((HTML_OBJECT_TYPE (self) == HTML_OBJECT_TYPE (with)
1000 	     /* FIXME */
1001 	     || (HTML_OBJECT_TYPE (self) == HTML_TYPE_TABLECELL && HTML_OBJECT_TYPE (with) == HTML_TYPE_CLUEV)
1002 	     || (HTML_OBJECT_TYPE (with) == HTML_TYPE_TABLECELL && HTML_OBJECT_TYPE (self) == HTML_TYPE_CLUEV))
1003 	    && (* HO_CLASS (self)->merge) (self, with, e, left, right, cursor)) {
1004 		if (with->parent)
1005 			html_object_remove_child (with->parent, with);
1006 		html_object_destroy (with);
1007 		return TRUE;
1008 	}
1009 	return FALSE;
1010 }
1011 
1012 void
html_object_remove_child(HTMLObject * self,HTMLObject * child)1013 html_object_remove_child (HTMLObject *self,
1014                           HTMLObject *child)
1015 {
1016 	g_assert (self);
1017 	g_assert (child);
1018 
1019 	(* HO_CLASS (self)->remove_child) (self, child);
1020 }
1021 
1022 void
html_object_split(HTMLObject * self,HTMLEngine * e,HTMLObject * child,gint offset,gint level,GList ** left,GList ** right)1023 html_object_split (HTMLObject *self,
1024                    HTMLEngine *e,
1025                    HTMLObject *child,
1026                    gint offset,
1027                    gint level,
1028                    GList **left,
1029                    GList **right)
1030 {
1031 	g_assert (self);
1032 
1033 	(* HO_CLASS (self)->split) (self, e, child, offset, level, left, right);
1034 }
1035 
1036 
1037 void
html_object_set_parent(HTMLObject * o,HTMLObject * parent)1038 html_object_set_parent (HTMLObject *o,
1039                         HTMLObject *parent)
1040 {
1041 	o->parent = parent;
1042 
1043 	/* parent change requires recalc of everything */
1044 	o->change = HTML_CHANGE_ALL;
1045 }
1046 
1047 static void
frame_offset(HTMLObject * o,gint * x_return,gint * y_return)1048 frame_offset (HTMLObject *o,
1049               gint *x_return,
1050               gint *y_return)
1051 {
1052 	if (html_object_is_frame (o)) {
1053 		HTMLEngine *e = html_object_get_engine (o, NULL);
1054 		*x_return -= e->x_offset;
1055 		*y_return -= e->y_offset;
1056 	}
1057 }
1058 
1059 void
html_object_calc_abs_position(HTMLObject * o,gint * x_return,gint * y_return)1060 html_object_calc_abs_position (HTMLObject *o,
1061                                gint *x_return,
1062                                gint *y_return)
1063 {
1064 	HTMLObject *p;
1065 
1066 	g_return_if_fail (o != NULL);
1067 
1068 	*x_return = o->x;
1069 	*y_return = o->y;
1070 
1071 	frame_offset (o, x_return, y_return);
1072 
1073 	for (p = o->parent; p != NULL; p = p->parent) {
1074 		*x_return += p->x;
1075 		*y_return += p->y - p->ascent;
1076 
1077 		frame_offset (p, x_return, y_return);
1078 	}
1079 }
1080 
1081 void
html_object_calc_abs_position_in_frame(HTMLObject * o,gint * x_return,gint * y_return)1082 html_object_calc_abs_position_in_frame (HTMLObject *o,
1083                                         gint *x_return,
1084                                         gint *y_return)
1085 {
1086 	HTMLObject *p;
1087 
1088 	g_return_if_fail (o != NULL);
1089 
1090 	*x_return = o->x;
1091 	*y_return = o->y;
1092 
1093 	frame_offset (o, x_return, y_return);
1094 
1095 	for (p = o->parent; p != NULL && !html_object_is_frame (p); p = p->parent) {
1096 		*x_return += p->x;
1097 		*y_return += p->y - p->ascent;
1098 
1099 		frame_offset (p, x_return, y_return);
1100 	}
1101 }
1102 
1103 GdkRectangle *
html_object_get_bounds(HTMLObject * o,GdkRectangle * bounds)1104 html_object_get_bounds (HTMLObject *o,
1105                         GdkRectangle *bounds)
1106 {
1107 	if (!bounds)
1108 		bounds = g_new (GdkRectangle, 1);
1109 
1110 	bounds->x = o->x;
1111 	bounds->y = o->y - o->ascent;
1112 	bounds->width = o->width;
1113 	bounds->height = o->ascent + o->descent;
1114 
1115 	return bounds;
1116 }
1117 
1118 gboolean
html_object_intersect(HTMLObject * o,GdkRectangle * result,gint x,gint y,gint width,gint height)1119 html_object_intersect (HTMLObject *o,
1120                        GdkRectangle *result,
1121                        gint x,
1122                        gint y,
1123                        gint width,
1124                        gint height)
1125 {
1126 	GdkRectangle b;
1127 	GdkRectangle a;
1128 
1129 	a.x = x;
1130 	a.y = y;
1131 	a.width = width;
1132 	a.height = height;
1133 
1134 	return gdk_rectangle_intersect (html_object_get_bounds (o, &b), &a, result);
1135 }
1136 
1137 
1138 /* Virtual methods.  */
1139 
1140 void
html_object_destroy(HTMLObject * self)1141 html_object_destroy (HTMLObject *self)
1142 {
1143 	(* HO_CLASS (self)->destroy) (self);
1144 }
1145 
1146 void
html_object_copy(HTMLObject * self,HTMLObject * dest)1147 html_object_copy (HTMLObject *self,
1148                   HTMLObject *dest)
1149 {
1150 	(* HO_CLASS (self)->copy) (self, dest);
1151 }
1152 
1153 void
html_object_draw(HTMLObject * o,HTMLPainter * p,gint x,gint y,gint width,gint height,gint tx,gint ty)1154 html_object_draw (HTMLObject *o,
1155                   HTMLPainter *p,
1156                   gint x,
1157                   gint y,
1158                   gint width,
1159                   gint height,
1160                   gint tx,
1161                   gint ty)
1162 {
1163 	(* HO_CLASS (o)->draw) (o, p, x, y, width, height, tx, ty);
1164 }
1165 
1166 gboolean
html_object_is_transparent(HTMLObject * self)1167 html_object_is_transparent (HTMLObject *self)
1168 {
1169 	g_return_val_if_fail (self != NULL, TRUE);
1170 
1171 	return (* HO_CLASS (self)->is_transparent) (self);
1172 }
1173 
1174 HTMLFitType
html_object_fit_line(HTMLObject * o,HTMLPainter * painter,gboolean start_of_line,gboolean first_run,gboolean next_to_floating,gint width_left)1175 html_object_fit_line (HTMLObject *o,
1176                       HTMLPainter *painter,
1177                       gboolean start_of_line,
1178                       gboolean first_run,
1179                       gboolean next_to_floating,
1180                       gint width_left)
1181 {
1182 	return (* HO_CLASS (o)->fit_line) (o, painter, start_of_line, first_run, next_to_floating, width_left);
1183 }
1184 
1185 gboolean
html_object_calc_size(HTMLObject * o,HTMLPainter * painter,GList ** changed_objs)1186 html_object_calc_size (HTMLObject *o,
1187                        HTMLPainter *painter,
1188                        GList **changed_objs)
1189 {
1190 	gboolean rv;
1191 
1192 	rv = (* HO_CLASS (o)->calc_size) (o, painter, changed_objs);
1193 	o->change &= ~HTML_CHANGE_SIZE;
1194 
1195 	return rv;
1196 }
1197 
1198 void
html_object_set_max_width(HTMLObject * o,HTMLPainter * painter,gint max_width)1199 html_object_set_max_width (HTMLObject *o,
1200                            HTMLPainter *painter,
1201                            gint max_width)
1202 {
1203 	(* HO_CLASS (o)->set_max_width) (o, painter, max_width);
1204 }
1205 
1206 void
html_object_set_max_height(HTMLObject * o,HTMLPainter * painter,gint max_height)1207 html_object_set_max_height (HTMLObject *o,
1208                             HTMLPainter *painter,
1209                             gint max_height)
1210 {
1211 	(* HO_CLASS (o)->set_max_height) (o, painter, max_height);
1212 }
1213 
1214 gint
html_object_get_left_margin(HTMLObject * self,HTMLPainter * painter,gint y,gboolean with_aligned)1215 html_object_get_left_margin (HTMLObject *self,
1216                              HTMLPainter *painter,
1217                              gint y,
1218                              gboolean with_aligned)
1219 {
1220 	return (* HO_CLASS (self)->get_left_margin) (self, painter, y, with_aligned);
1221 }
1222 
1223 gint
html_object_get_right_margin(HTMLObject * self,HTMLPainter * painter,gint y,gboolean with_aligned)1224 html_object_get_right_margin (HTMLObject *self,
1225                               HTMLPainter *painter,
1226                               gint y,
1227                               gboolean with_aligned)
1228 {
1229 	return (* HO_CLASS (self)->get_right_margin) (self, painter, y, with_aligned);
1230 }
1231 
1232 static void
set_painter_forall(HTMLObject * o,HTMLEngine * e,gpointer data)1233 set_painter_forall (HTMLObject *o,
1234                     HTMLEngine *e,
1235                     gpointer data)
1236 {
1237 	(* HO_CLASS (o)->set_painter) (o, HTML_PAINTER (data));
1238 }
1239 
1240 void
html_object_set_painter(HTMLObject * o,HTMLPainter * painter)1241 html_object_set_painter (HTMLObject *o,
1242                          HTMLPainter *painter)
1243 {
1244 	html_object_forall (o, NULL, set_painter_forall, painter);
1245 }
1246 
1247 void
html_object_reset(HTMLObject * o)1248 html_object_reset (HTMLObject *o)
1249 {
1250 	(* HO_CLASS (o)->reset) (o);
1251 }
1252 
1253 gint
html_object_calc_min_width(HTMLObject * o,HTMLPainter * painter)1254 html_object_calc_min_width (HTMLObject *o,
1255                             HTMLPainter *painter)
1256 {
1257 	if (o->change & HTML_CHANGE_MIN_WIDTH) {
1258 		o->min_width = (* HO_CLASS (o)->calc_min_width) (o, painter);
1259 		o->change &= ~HTML_CHANGE_MIN_WIDTH;
1260 	}
1261 	return o->min_width;
1262 }
1263 
1264 gint
html_object_calc_preferred_width(HTMLObject * o,HTMLPainter * painter)1265 html_object_calc_preferred_width (HTMLObject *o,
1266                                   HTMLPainter *painter)
1267 {
1268 	if (o->change & HTML_CHANGE_PREF_WIDTH) {
1269 		o->pref_width = (* HO_CLASS (o)->calc_preferred_width) (o, painter);
1270 		o->change &= ~HTML_CHANGE_PREF_WIDTH;
1271 	}
1272 	return o->pref_width;
1273 }
1274 
1275 #if 0
1276 gint
1277 html_object_get_uris (HTMLObject *o,
1278                       gchar **link,
1279                       gchar **target,
1280                       gchar **src)
1281 {
1282 	return TRUE;
1283 }
1284 #endif
1285 
1286 const gchar *
html_object_get_url(HTMLObject * o,gint offset)1287 html_object_get_url (HTMLObject *o,
1288                      gint offset)
1289 {
1290 	return (* HO_CLASS (o)->get_url) (o, offset);
1291 }
1292 
1293 const gchar *
html_object_get_target(HTMLObject * o,gint offset)1294 html_object_get_target (HTMLObject *o,
1295                         gint offset)
1296 {
1297 	return (* HO_CLASS (o)->get_target) (o, offset);
1298 }
1299 
1300 gchar *
html_object_get_complete_url(HTMLObject * o,gint offset)1301 html_object_get_complete_url (HTMLObject *o,
1302                               gint offset)
1303 {
1304 	const gchar *url, *target;
1305 
1306 	url = html_object_get_url (o, offset);
1307 	target = html_object_get_target (o, offset);
1308 	return url || target ? g_strconcat (url ? url : "#", url ? (target && *target ? "#" : NULL) : target,
1309 					      url ? target : NULL, NULL) : NULL;
1310 }
1311 
1312 const gchar *
html_object_get_src(HTMLObject * o)1313 html_object_get_src (HTMLObject *o)
1314 {
1315 	return (* HO_CLASS (o)->get_src) (o);
1316 }
1317 
1318 HTMLAnchor *
html_object_find_anchor(HTMLObject * o,const gchar * name,gint * x,gint * y)1319 html_object_find_anchor (HTMLObject *o,
1320                          const gchar *name,
1321                          gint *x,
1322                          gint *y)
1323 {
1324 	return (* HO_CLASS (o)->find_anchor) (o, name, x, y);
1325 }
1326 
1327 void
html_object_set_bg_color(HTMLObject * o,GdkColor * color)1328 html_object_set_bg_color (HTMLObject *o,
1329                           GdkColor *color)
1330 {
1331 	(* HO_CLASS (o)->set_bg_color) (o, color);
1332 }
1333 
1334 GdkColor *
html_object_get_bg_color(HTMLObject * o,HTMLPainter * p)1335 html_object_get_bg_color (HTMLObject *o,
1336                           HTMLPainter *p)
1337 {
1338 	return (* HO_CLASS (o)->get_bg_color) (o, p);
1339 }
1340 
1341 HTMLObject *
html_object_check_point(HTMLObject * self,HTMLPainter * painter,gint x,gint y,guint * offset_return,gboolean for_cursor)1342 html_object_check_point (HTMLObject *self,
1343                          HTMLPainter *painter,
1344                          gint x,
1345                          gint y,
1346                          guint *offset_return,
1347                          gboolean for_cursor)
1348 {
1349 	if (self->width == 0 || self->ascent + self->descent == 0)
1350 		return NULL;
1351 
1352 	return (* HO_CLASS (self)->check_point) (self, painter, x, y, offset_return, for_cursor);
1353 }
1354 
1355 gboolean
html_object_relayout(HTMLObject * self,HTMLEngine * engine,HTMLObject * child)1356 html_object_relayout (HTMLObject *self,
1357                       HTMLEngine *engine,
1358                       HTMLObject *child)
1359 {
1360 	g_return_val_if_fail (self != NULL, TRUE);
1361 	return (* HO_CLASS (self)->relayout) (self, engine, child);
1362 }
1363 
1364 HTMLVAlignType
html_object_get_valign(HTMLObject * self)1365 html_object_get_valign (HTMLObject *self)
1366 {
1367 	g_return_val_if_fail (self != NULL, HTML_VALIGN_BOTTOM);
1368 
1369 	return (* HO_CLASS (self)->get_valign) (self);
1370 }
1371 
1372 gboolean
html_object_accepts_cursor(HTMLObject * self)1373 html_object_accepts_cursor (HTMLObject *self)
1374 {
1375 	return (* HO_CLASS (self)->accepts_cursor) (self);
1376 }
1377 
1378 /* Warning: `calc_size()' must have been called on `self' before this so that
1379  * this works correctly.  */
1380 void
html_object_get_cursor(HTMLObject * self,HTMLPainter * painter,guint offset,gint * x1,gint * y1,gint * x2,gint * y2)1381 html_object_get_cursor (HTMLObject *self,
1382                         HTMLPainter *painter,
1383                         guint offset,
1384                         gint *x1,
1385                         gint *y1,
1386                         gint *x2,
1387                         gint *y2)
1388 {
1389 	(* HO_CLASS (self)->get_cursor) (self, painter, offset, x1, y1, x2, y2);
1390 
1391 	if (*y2 - *y1 > *y2 - self->ascent)
1392 		*y2 = *y1 + 20;
1393 
1394 	if (!html_object_is_text (self) && *y2 - *y1 < 10) {
1395 		gint missing = 10 - (*y2 - *y1);
1396 
1397 		*y1 -= (missing >> 1) + ((missing >> 1) & 1);
1398 		*y2 += missing >> 1;
1399 	}
1400 }
1401 
1402 /* Warning: `calc_size()' must have been called on `self' before this so that
1403  * this works correctly.  */
1404 void
html_object_get_cursor_base(HTMLObject * self,HTMLPainter * painter,guint offset,gint * x,gint * y)1405 html_object_get_cursor_base (HTMLObject *self,
1406                              HTMLPainter *painter,
1407                              guint offset,
1408                              gint *x,
1409                              gint *y)
1410 {
1411 	(* HO_CLASS (self)->get_cursor_base) (self, painter, offset, x, y);
1412 }
1413 
1414 
1415 gboolean
html_object_select_range(HTMLObject * self,HTMLEngine * engine,guint start,gint length,gboolean queue_draw)1416 html_object_select_range (HTMLObject *self,
1417                           HTMLEngine *engine,
1418                           guint start,
1419                           gint length,
1420                           gboolean queue_draw)
1421 {
1422 	return (* HO_CLASS (self)->select_range) (self, engine, start, length, queue_draw);
1423 }
1424 
1425 void
html_object_append_selection_string(HTMLObject * self,GString * buffer)1426 html_object_append_selection_string (HTMLObject *self,
1427                                      GString *buffer)
1428 {
1429 	g_return_if_fail (self != NULL);
1430 	g_return_if_fail (buffer != NULL);
1431 
1432 	(* HO_CLASS (self)->append_selection_string) (self, buffer);
1433 }
1434 
1435 HTMLEngine *
html_object_get_engine(HTMLObject * self,HTMLEngine * e)1436 html_object_get_engine (HTMLObject *self,
1437                         HTMLEngine *e)
1438 {
1439 	return (* HO_CLASS (self)->get_engine) (self, e);
1440 }
1441 
1442 HTMLEngine *
html_object_engine(HTMLObject * o,HTMLEngine * e)1443 html_object_engine (HTMLObject *o,
1444                     HTMLEngine *e)
1445 {
1446 	while (o) {
1447 		e = html_object_get_engine (o, e);
1448 		if (html_object_is_frame (o))
1449 			break;
1450 		o = o->parent;
1451 	}
1452 
1453 	return e;
1454 }
1455 
1456 void
html_object_forall(HTMLObject * self,HTMLEngine * e,HTMLObjectForallFunc func,gpointer data)1457 html_object_forall (HTMLObject *self,
1458                     HTMLEngine *e,
1459                     HTMLObjectForallFunc func,
1460                     gpointer data)
1461 {
1462 	(* HO_CLASS (self)->forall) (self, e, func, data);
1463 }
1464 
1465 /* Ugly.  We should have an `is_a' implementation.  */
1466 gboolean
html_object_is_container(HTMLObject * self)1467 html_object_is_container (HTMLObject *self)
1468 {
1469 	return (* HO_CLASS (self)->is_container) (self);
1470 }
1471 
1472 
1473 /* Ugly.  We should have an `is_a' implementation.  */
1474 
1475 gboolean
html_object_is_text(HTMLObject * object)1476 html_object_is_text (HTMLObject *object)
1477 {
1478 	HTMLType type;
1479 
1480 	g_return_val_if_fail (object != NULL, FALSE);
1481 
1482 	type = HTML_OBJECT_TYPE (object);
1483 
1484 	return (type == HTML_TYPE_TEXT || type == HTML_TYPE_LINKTEXT);
1485 }
1486 
1487 gboolean
html_object_is_clue(HTMLObject * object)1488 html_object_is_clue (HTMLObject *object)
1489 {
1490 	HTMLType type;
1491 
1492 	g_return_val_if_fail (object != NULL, FALSE);
1493 
1494 	type = HTML_OBJECT_TYPE (object);
1495 
1496 	return (type == HTML_TYPE_CLUE || type == HTML_TYPE_CLUEV || type == HTML_TYPE_TABLECELL
1497 		|| type == HTML_TYPE_CLUEFLOW || type == HTML_TYPE_CLUEALIGNED);
1498 }
1499 
1500 HTMLObject *
html_object_next_not_type(HTMLObject * object,HTMLType t)1501 html_object_next_not_type (HTMLObject *object,
1502                            HTMLType t)
1503 {
1504 	HTMLObject *p;
1505 
1506 	g_return_val_if_fail (object != NULL, NULL);
1507 	g_return_val_if_fail (object->parent, NULL);
1508 
1509 	p = html_object_next (object->parent, object);
1510 	while (p && HTML_OBJECT_TYPE (p) == t)
1511 		p = html_object_next (p->parent, p);
1512 
1513 	return p;
1514 }
1515 
1516 HTMLObject *
html_object_prev_not_type(HTMLObject * object,HTMLType t)1517 html_object_prev_not_type (HTMLObject *object,
1518                            HTMLType t)
1519 {
1520 	HTMLObject *p;
1521 
1522 	g_return_val_if_fail (object != NULL, NULL);
1523 	g_return_val_if_fail (object->parent, NULL);
1524 
1525 	p = html_object_prev (object->parent, object);
1526 	while (p && HTML_OBJECT_TYPE (p) == t)
1527 		p = html_object_prev (p->parent, p);
1528 
1529 	return p;
1530 }
1531 
1532 HTMLObject *
html_object_next_not_slave(HTMLObject * object)1533 html_object_next_not_slave (HTMLObject *object)
1534 {
1535 	return html_object_next_not_type (object, HTML_TYPE_TEXTSLAVE);
1536 }
1537 
1538 HTMLObject *
html_object_prev_not_slave(HTMLObject * object)1539 html_object_prev_not_slave (HTMLObject *object)
1540 {
1541 	return html_object_prev_not_type (object, HTML_TYPE_TEXTSLAVE);
1542 }
1543 
1544 
1545 gboolean
html_object_save(HTMLObject * self,HTMLEngineSaveState * state)1546 html_object_save (HTMLObject *self,
1547                   HTMLEngineSaveState *state)
1548 {
1549 	return (* HO_CLASS (self)->save) (self, state);
1550 }
1551 
1552 gboolean
html_object_save_plain(HTMLObject * self,HTMLEngineSaveState * state,gint requested_width)1553 html_object_save_plain (HTMLObject *self,
1554                         HTMLEngineSaveState *state,
1555                         gint requested_width)
1556 {
1557 	return (* HO_CLASS (self)->save_plain) (self, state, requested_width);
1558 }
1559 
1560 gint
html_object_check_page_split(HTMLObject * self,HTMLPainter * p,gint y)1561 html_object_check_page_split (HTMLObject *self,
1562                                HTMLPainter *p,
1563                                gint y)
1564 {
1565 	g_return_val_if_fail (self != NULL, 0);
1566 
1567 	return (* HO_CLASS (self)->check_page_split) (self, p, y);
1568 }
1569 
1570 void
html_object_change_set(HTMLObject * self,HTMLChangeFlags f)1571 html_object_change_set (HTMLObject *self,
1572                         HTMLChangeFlags f)
1573 {
1574 	HTMLObject *obj = self;
1575 
1576 	g_assert (self != NULL);
1577 
1578 	if (f != HTML_CHANGE_NONE) {
1579 		while (obj) {
1580 			obj->change |= f;
1581 			obj = obj->parent;
1582 		}
1583 	}
1584 }
1585 
1586 static void
change(HTMLObject * o,HTMLEngine * e,gpointer data)1587 change (HTMLObject *o,
1588         HTMLEngine *e,
1589         gpointer data)
1590 {
1591 	o->change |= GPOINTER_TO_INT (data);
1592 }
1593 
1594 void
html_object_change_set_down(HTMLObject * self,HTMLChangeFlags f)1595 html_object_change_set_down (HTMLObject *self,
1596                              HTMLChangeFlags f)
1597 {
1598 	html_object_forall (self, NULL, (HTMLObjectForallFunc) change, GINT_TO_POINTER (f));
1599 }
1600 
1601 gboolean
html_object_search(HTMLObject * self,HTMLSearch * info)1602 html_object_search (HTMLObject *self,
1603                     HTMLSearch *info)
1604 {
1605 	return (* HO_CLASS (self)->search) (self, info);
1606 }
1607 
1608 gboolean
html_object_search_next(HTMLObject * self,HTMLSearch * info)1609 html_object_search_next (HTMLObject *self,
1610                          HTMLSearch *info)
1611 {
1612 	return (* HO_CLASS (self)->search_next) (self, info);
1613 }
1614 
1615 HTMLObject *
html_object_set_link(HTMLObject * self,HTMLColor * color,const gchar * url,const gchar * target)1616 html_object_set_link (HTMLObject *self,
1617                       HTMLColor *color,
1618                       const gchar *url,
1619                       const gchar *target)
1620 {
1621 	return (HO_CLASS (self)->set_link) ? (* HO_CLASS (self)->set_link) (self, color, url, target) : NULL;
1622 }
1623 
1624 HTMLObject *
html_object_remove_link(HTMLObject * self,HTMLColor * color)1625 html_object_remove_link (HTMLObject *self,
1626                          HTMLColor *color)
1627 {
1628 	return (HO_CLASS (self)->set_link) ? (* HO_CLASS (self)->set_link) (self, color, NULL, NULL) : NULL;
1629 }
1630 
1631 guint
html_object_get_length(HTMLObject * self)1632 html_object_get_length (HTMLObject *self)
1633 {
1634 	return (* HO_CLASS (self)->get_length) (self);
1635 }
1636 
1637 guint
html_object_get_line_length(HTMLObject * self,HTMLPainter * p,gint line_offset)1638 html_object_get_line_length (HTMLObject *self,
1639                              HTMLPainter *p,
1640                              gint line_offset)
1641 {
1642 	return (* HO_CLASS (self)->get_line_length) (self, p, line_offset);
1643 }
1644 
1645 guint
html_object_get_recursive_length(HTMLObject * self)1646 html_object_get_recursive_length (HTMLObject *self)
1647 {
1648 	return (* HO_CLASS (self)->get_recursive_length) (self);
1649 }
1650 
1651 HTMLObject *
html_object_next_by_type(HTMLObject * self,HTMLType t)1652 html_object_next_by_type (HTMLObject *self,
1653                           HTMLType t)
1654 {
1655 	HTMLObject *next;
1656 
1657 	g_assert (self);
1658 
1659 	next = self->next;
1660 	while (next && HTML_OBJECT_TYPE (next) != t)
1661 		next = next->next;
1662 
1663 	return next;
1664 }
1665 
1666 HTMLObject *
html_object_prev_by_type(HTMLObject * self,HTMLType t)1667 html_object_prev_by_type (HTMLObject *self,
1668                           HTMLType t)
1669 {
1670 	HTMLObject *prev;
1671 
1672 	g_assert (self);
1673 
1674 	prev = self->prev;
1675 	while (prev && HTML_OBJECT_TYPE (prev) != t)
1676 		prev = prev->prev;
1677 
1678 	return prev;
1679 }
1680 
1681 /* Movement functions */
1682 
1683 HTMLObject *
html_object_next(HTMLObject * self,HTMLObject * child)1684 html_object_next (HTMLObject *self,
1685                   HTMLObject *child)
1686 {
1687 	return (* HO_CLASS (self)->next) (self, child);
1688 }
1689 
1690 HTMLObject *
html_object_prev(HTMLObject * self,HTMLObject * child)1691 html_object_prev (HTMLObject *self,
1692                   HTMLObject *child)
1693 {
1694 	return (* HO_CLASS (self)->prev) (self, child);
1695 }
1696 
1697 HTMLObject *
html_object_head(HTMLObject * self)1698 html_object_head (HTMLObject *self)
1699 {
1700 	return (* HO_CLASS (self)->head) (self);
1701 }
1702 
1703 HTMLObject *
html_object_tail(HTMLObject * self)1704 html_object_tail (HTMLObject *self)
1705 {
1706 	return (* HO_CLASS (self)->tail) (self);
1707 }
1708 
1709 HTMLObject *
html_object_tail_not_slave(HTMLObject * self)1710 html_object_tail_not_slave (HTMLObject *self)
1711 {
1712 	HTMLObject *o = html_object_tail (self);
1713 
1714 	if (o && HTML_OBJECT_TYPE (o) == HTML_TYPE_TEXTSLAVE)
1715 		o = html_object_prev_not_slave (o);
1716 	return o;
1717 }
1718 
1719 gboolean
html_object_cursor_forward(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)1720 html_object_cursor_forward (HTMLObject *self,
1721                             HTMLCursor *cursor,
1722                             HTMLEngine *engine)
1723 {
1724 	return (* HO_CLASS (self)->cursor_forward) (self, cursor, engine);
1725 }
1726 
1727 gboolean
html_object_cursor_forward_one(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)1728 html_object_cursor_forward_one (HTMLObject *self,
1729                                 HTMLCursor *cursor,
1730                                 HTMLEngine *engine)
1731 {
1732 	return (* HO_CLASS (self)->cursor_forward_one) (self, cursor, engine);
1733 }
1734 
1735 gboolean
html_object_cursor_backward(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)1736 html_object_cursor_backward (HTMLObject *self,
1737                              HTMLCursor *cursor,
1738                              HTMLEngine *engine)
1739 {
1740 	return (* HO_CLASS (self)->cursor_backward) (self, cursor, engine);
1741 }
1742 
1743 gboolean
html_object_cursor_backward_one(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)1744 html_object_cursor_backward_one (HTMLObject *self,
1745                                  HTMLCursor *cursor,
1746                                  HTMLEngine *engine)
1747 {
1748 	return (* HO_CLASS (self)->cursor_backward_one) (self, cursor, engine);
1749 }
1750 
1751 gboolean
html_object_cursor_right(HTMLObject * self,HTMLPainter * painter,HTMLCursor * cursor)1752 html_object_cursor_right (HTMLObject *self,
1753                           HTMLPainter *painter,
1754                           HTMLCursor *cursor)
1755 {
1756 	return (* HO_CLASS (self)->cursor_right) (self, painter, cursor);
1757 }
1758 
1759 gboolean
html_object_cursor_left(HTMLObject * self,HTMLPainter * painter,HTMLCursor * cursor)1760 html_object_cursor_left (HTMLObject *self,
1761                          HTMLPainter *painter,
1762                          HTMLCursor *cursor)
1763 {
1764 	return (* HO_CLASS (self)->cursor_left) (self, painter, cursor);
1765 }
1766 
1767 gboolean
html_object_backspace(HTMLObject * self,HTMLCursor * cursor,HTMLEngine * engine)1768 html_object_backspace (HTMLObject *self,
1769                        HTMLCursor *cursor,
1770                        HTMLEngine *engine)
1771 {
1772 	return (* HO_CLASS (self)->backspace) (self, cursor, engine);
1773 }
1774 
1775 /*********************
1776  * movement on leafs
1777  */
1778 
1779 /* go up in tree so long as we can get object in neighborhood given by function next_fn */
1780 
1781 static HTMLObject *
next_object_uptree(HTMLObject * obj,HTMLObject * (* next_fn)(HTMLObject *,HTMLObject *))1782 next_object_uptree (HTMLObject *obj,
1783                     HTMLObject * (*next_fn) (HTMLObject *,
1784                     HTMLObject *))
1785 {
1786 	HTMLObject *next = NULL;
1787 
1788 	while (obj->parent && !(next = (*next_fn) (obj->parent, obj)))
1789 		obj = obj->parent;
1790 
1791 	return next;
1792 }
1793 
1794 /* go down in tree to leaf in way given by down_fn children */
1795 
1796 static HTMLObject *
move_object_downtree(HTMLObject * obj,HTMLObject * (* down_fn)(HTMLObject *))1797 move_object_downtree (HTMLObject *obj,
1798                       HTMLObject * (*down_fn) (HTMLObject *))
1799 {
1800 	HTMLObject *down;
1801 
1802 	while ((down = (*down_fn) (obj)))
1803 		obj = down;
1804 
1805 	return obj;
1806 }
1807 
1808 static HTMLObject *
move_object(HTMLObject * obj,HTMLObject * (* next_fn)(HTMLObject *,HTMLObject *),HTMLObject * (* down_fn)(HTMLObject *))1809 move_object (HTMLObject *obj,
1810              HTMLObject * (*next_fn) (HTMLObject *,
1811              HTMLObject *),
1812              HTMLObject * (*down_fn) (HTMLObject *))
1813 {
1814 	obj = next_object_uptree (obj, next_fn);
1815 	if (obj)
1816 		obj = move_object_downtree (obj, down_fn);
1817 	return obj;
1818 }
1819 
1820 HTMLObject *
html_object_next_leaf(HTMLObject * self)1821 html_object_next_leaf (HTMLObject *self)
1822 {
1823 	return move_object (self, html_object_next, html_object_head);
1824 }
1825 
1826 HTMLObject *
html_object_next_leaf_not_type(HTMLObject * self,HTMLType t)1827 html_object_next_leaf_not_type (HTMLObject *self,
1828                                 HTMLType t)
1829 {
1830 	HTMLObject *rv = self;
1831 	while ((rv = html_object_next_leaf (rv)) && HTML_OBJECT_TYPE (rv) == t);
1832 
1833 	return rv;
1834 }
1835 
1836 HTMLObject *
html_object_prev_leaf(HTMLObject * self)1837 html_object_prev_leaf (HTMLObject *self)
1838 {
1839 	return move_object (self, html_object_prev, html_object_tail);
1840 }
1841 
1842 HTMLObject *
html_object_prev_leaf_not_type(HTMLObject * self,HTMLType t)1843 html_object_prev_leaf_not_type (HTMLObject *self,
1844                                 HTMLType t)
1845 {
1846 	HTMLObject *rv = self;
1847 	while ((rv = html_object_prev_leaf (rv)) && HTML_OBJECT_TYPE (rv) == t);
1848 
1849 	return rv;
1850 }
1851 
1852 /* movement on cursor accepting objects */
1853 
1854 /* go up in tree so long as we can get object in neighborhood given by function next_fn */
1855 
1856 static HTMLObject *
next_object_uptree_cursor(HTMLObject * obj,HTMLObject * (* next_fn)(HTMLObject *))1857 next_object_uptree_cursor (HTMLObject *obj,
1858                            HTMLObject * (*next_fn) (HTMLObject *))
1859 {
1860 	HTMLObject *next = NULL;
1861 
1862 	while (obj->parent && !(next = (*next_fn) (obj))) {
1863 		obj = obj->parent;
1864 		if (html_object_accepts_cursor (obj))
1865 			return obj;
1866 	}
1867 
1868 	return next;
1869 }
1870 
1871 /* go down in tree to leaf in way given by down_fn children */
1872 
1873 static HTMLObject *
move_object_downtree_cursor(HTMLObject * obj,HTMLObject * (* down_fn)(HTMLObject *),HTMLObject * (* next_fn)(HTMLObject *))1874 move_object_downtree_cursor (HTMLObject *obj,
1875                              HTMLObject * (*down_fn) (HTMLObject *),
1876                              HTMLObject * (*next_fn) (HTMLObject *))
1877 {
1878 	HTMLObject *last_obj = obj;
1879 
1880 	while ((obj = (*down_fn) (obj))) {
1881 		if (html_object_accepts_cursor (obj))
1882 			break;
1883 		last_obj = obj;
1884 	}
1885 
1886 	if (!obj && last_obj) {
1887 		obj = last_obj;
1888 
1889 		while ((obj = (*next_fn) (obj))) {
1890 			if (html_object_accepts_cursor (obj))
1891 				break;
1892 			last_obj = obj;
1893 			if ((obj = move_object_downtree_cursor (obj, down_fn, next_fn)))
1894 				break;
1895 			obj = last_obj;
1896 		}
1897 	}
1898 
1899 	return obj;
1900 }
1901 
1902 static HTMLObject *
move_object_cursor(HTMLObject * obj,gint * offset,gboolean forward,HTMLObject * (* next_fn)(HTMLObject *),HTMLObject * (* down_fn)(HTMLObject *))1903 move_object_cursor (HTMLObject *obj,
1904                     gint *offset,
1905                     gboolean forward,
1906                     HTMLObject * (*next_fn) (HTMLObject *), HTMLObject * (*down_fn) (HTMLObject *))
1907 {
1908 	HTMLObject *down, *before;
1909 
1910 	do {
1911 		gboolean found = FALSE;
1912 		if (((*offset == 0 && forward) || (*offset && !forward)) && html_object_is_container (obj))
1913 			if ((down = (*down_fn) (obj))) {
1914 				down = move_object_downtree_cursor (down, down_fn, next_fn);
1915 				if (down) {
1916 					if (html_object_is_container (down))
1917 						*offset = forward ? 0 : 1;
1918 					return down;
1919 				}
1920 			}
1921 
1922 		before = obj;
1923 		do {
1924 			obj = next_object_uptree_cursor (obj, next_fn);
1925 			if (obj) {
1926 				if (html_object_accepts_cursor (obj)) {
1927 					if (html_object_is_container (obj))
1928 						*offset = before->parent == obj->parent
1929 							? forward ? 0 : 1
1930 							: forward ? 1 : 0;
1931 					found = TRUE;
1932 				} else {
1933 					HTMLObject *down;
1934 					down = move_object_downtree_cursor (obj, down_fn, next_fn);
1935 					if (down) {
1936 						if (html_object_is_container (down))
1937 							*offset = forward ? 0 : 1;
1938 						obj = down;
1939 						found = TRUE;
1940 					}
1941 				}
1942 			}
1943 		} while (obj && !found);
1944 	} while (obj && !html_object_accepts_cursor (obj));
1945 
1946 	return obj;
1947 }
1948 
1949 HTMLObject *
html_object_next_cursor(HTMLObject * self,gint * offset)1950 html_object_next_cursor (HTMLObject *self,
1951                          gint *offset)
1952 {
1953 	return move_object_cursor (self, offset, TRUE, html_object_next_not_slave, html_object_head);
1954 }
1955 
1956 HTMLObject *
html_object_prev_cursor(HTMLObject * self,gint * offset)1957 html_object_prev_cursor (HTMLObject *self,
1958                          gint *offset)
1959 {
1960 	return move_object_cursor (self, offset, FALSE, html_object_prev_not_slave, html_object_tail_not_slave);
1961 }
1962 
1963 /***/
1964 
1965 guint
html_object_get_bytes(HTMLObject * self)1966 html_object_get_bytes (HTMLObject *self)
1967 {
1968 	return html_object_is_text (self) ? html_text_get_bytes (HTML_TEXT (self)) : html_object_get_length (self);
1969 }
1970 
1971 guint
html_object_get_index(HTMLObject * self,guint offset)1972 html_object_get_index (HTMLObject *self,
1973                        guint offset)
1974 {
1975 	return html_object_is_text (self) ? html_text_get_index (HTML_TEXT (self), offset) : offset;
1976 }
1977 
1978 void
html_object_set_data_nocp(HTMLObject * object,const gchar * key,const gchar * value)1979 html_object_set_data_nocp (HTMLObject *object,
1980                            const gchar *key,
1981                            const gchar *value)
1982 {
1983 	g_datalist_set_data_full (&object->object_data_nocp, key, g_strdup (value), g_free);
1984 }
1985 
1986 void
html_object_set_data_full_nocp(HTMLObject * object,const gchar * key,gconstpointer value,GDestroyNotify func)1987 html_object_set_data_full_nocp (HTMLObject *object,
1988                                 const gchar *key,
1989                                 gconstpointer value,
1990                                 GDestroyNotify func)
1991 {
1992 	g_datalist_set_data_full (&object->object_data_nocp, key, (gpointer) value, func);
1993 }
1994 
1995 gpointer
html_object_get_data_nocp(HTMLObject * object,const gchar * key)1996 html_object_get_data_nocp (HTMLObject *object,
1997                            const gchar *key)
1998 {
1999 	return g_datalist_get_data (&object->object_data_nocp, key);
2000 }
2001 
2002 void
html_object_set_data(HTMLObject * object,const gchar * key,const gchar * value)2003 html_object_set_data (HTMLObject *object,
2004                       const gchar *key,
2005                       const gchar *value)
2006 {
2007 	g_datalist_set_data_full (&object->object_data, key, g_strdup (value), g_free);
2008 }
2009 
2010 void
html_object_set_data_full(HTMLObject * object,const gchar * key,gconstpointer value,GDestroyNotify func)2011 html_object_set_data_full (HTMLObject *object,
2012                            const gchar *key,
2013                            gconstpointer value,
2014                            GDestroyNotify func)
2015 {
2016 	g_datalist_set_data_full (&object->object_data, key, (gpointer) value, func);
2017 }
2018 
2019 gpointer
html_object_get_data(HTMLObject * object,const gchar * key)2020 html_object_get_data (HTMLObject *object,
2021                       const gchar *key)
2022 {
2023 	return g_datalist_get_data (&object->object_data, key);
2024 }
2025 
2026 static void
copy_data(GQuark key_id,gpointer data,gpointer user_data)2027 copy_data (GQuark key_id,
2028            gpointer data,
2029            gpointer user_data)
2030 {
2031 	HTMLObject *o = HTML_OBJECT (user_data);
2032 
2033 	g_datalist_id_set_data_full (&o->object_data,
2034 				     key_id,
2035 				     g_strdup ((gchar *) data), g_free);
2036 }
2037 
2038 void
html_object_copy_data_from_object(HTMLObject * dst,HTMLObject * src)2039 html_object_copy_data_from_object (HTMLObject *dst,
2040                                    HTMLObject *src)
2041 {
2042 	g_datalist_foreach (&src->object_data, copy_data, dst);
2043 }
2044 
2045 static void
object_save_data(GQuark key_id,gpointer data,gpointer user_data)2046 object_save_data (GQuark key_id,
2047                   gpointer data,
2048                   gpointer user_data)
2049 {
2050 	HTMLEngineSaveState *state = (HTMLEngineSaveState *) user_data;
2051 	const gchar *str;
2052 
2053 	str = html_engine_get_class_data (state->engine, state->save_data_class_name, g_quark_to_string (key_id));
2054 	/* printf ("object %s %s\n", g_quark_to_string (key_id), str); */
2055 	if (!str) {
2056 		/* printf ("save %s %s -> %s\n", state->save_data_class_name, g_quark_to_string (key_id), (gchar *) data); */
2057 		html_engine_save_delims_and_vals (state,
2058 				"<!--+GtkHTML:<DATA class=\"", state->save_data_class_name,
2059 				"\" key=\"", g_quark_to_string (key_id),
2060 				"\" value=\"", (gchar *) data,
2061 				"\">-->", NULL);
2062 		html_engine_set_class_data (state->engine, state->save_data_class_name, g_quark_to_string (key_id), data);
2063 	}
2064 }
2065 
2066 static void
handle_object_data(gpointer key,gpointer value,gpointer data)2067 handle_object_data (gpointer key,
2068                     gpointer value,
2069                     gpointer data)
2070 {
2071 	HTMLEngineSaveState *state = (HTMLEngineSaveState *) data;
2072 	gchar *str;
2073 
2074 	str = html_object_get_data (HTML_OBJECT (state->save_data_object), key);
2075 	/* printf ("handle: %s %s %s %s\n", state->save_data_class_name, key, value, str); */
2076 	if (!str) {
2077 		/* printf ("clear\n"); */
2078 		html_engine_save_delims_and_vals (state,
2079 				"<!--+GtkHTML:<DATA class=\"", state->save_data_class_name,
2080 				"\" clear=\"", (gchar *) key,
2081 				"\">-->", NULL);
2082 		state->data_to_remove = g_slist_prepend (state->data_to_remove, key);
2083 	} else if (strcmp (value, str)) {
2084 		/* printf ("change\n"); */
2085 		html_engine_save_delims_and_vals (state,
2086 				"<!--+GtkHTML:<DATA class=\"", state->save_data_class_name,
2087 				"\" key=\"", (gchar *) key,
2088 				"\" value=\"", str,
2089 				"\">-->", NULL);
2090 		html_engine_set_class_data (state->engine, state->save_data_class_name, key, value);
2091 	}
2092 }
2093 
2094 static void
clear_data(gchar * key,HTMLEngineSaveState * state)2095 clear_data (gchar *key,
2096             HTMLEngineSaveState *state)
2097 {
2098 	html_engine_clear_class_data (state->engine, state->save_data_class_name, key);
2099 }
2100 
2101 gboolean
html_object_save_data(HTMLObject * self,HTMLEngineSaveState * state)2102 html_object_save_data (HTMLObject *self,
2103                        HTMLEngineSaveState *state)
2104 {
2105 	if (state->engine->save_data) {
2106 		GHashTable *t;
2107 		state->save_data_class_name = html_type_name (self->klass->type);
2108 		state->save_data_object = self;
2109 		t = html_engine_get_class_table (state->engine, state->save_data_class_name);
2110 		if (t) {
2111 			state->data_to_remove = NULL;
2112 			g_hash_table_foreach (t, handle_object_data, state);
2113 			g_slist_foreach (state->data_to_remove, (GFunc) clear_data, state);
2114 			g_slist_free (state->data_to_remove);
2115 			state->data_to_remove = NULL;
2116 		}
2117 		g_datalist_foreach (&self->object_data, object_save_data, state);
2118 	}
2119 
2120 	return TRUE;
2121 }
2122 
2123 GList *
html_object_get_bound_list(HTMLObject * self,GList * list)2124 html_object_get_bound_list (HTMLObject *self,
2125                             GList *list)
2126 {
2127 	return list && list->next
2128 		? (HTML_OBJECT (list->data) == self ? list->next : NULL)
2129 		: NULL;
2130 }
2131 
2132 void
html_object_move_cursor_before_remove(HTMLObject * o,HTMLEngine * e)2133 html_object_move_cursor_before_remove (HTMLObject *o,
2134                                        HTMLEngine *e)
2135 {
2136 	if (e->cursor->object == o) {
2137 		if (html_object_next_not_slave (o))
2138 			e->cursor->object = html_object_next_not_slave (o);
2139 		else
2140 			e->cursor->object = html_object_prev_not_slave (o);
2141 	}
2142 }
2143 
2144 gboolean
html_object_could_remove_whole(HTMLObject * o,GList * from,GList * to,GList * left,GList * right)2145 html_object_could_remove_whole (HTMLObject *o,
2146                                 GList *from,
2147                                 GList *to,
2148                                 GList *left,
2149                                 GList *right)
2150 {
2151 	return ((!from && !to)
2152 		|| html_object_next_not_slave (HTML_OBJECT (o))
2153 		|| html_object_prev_not_slave (HTML_OBJECT (o)))
2154 		&& ((!left  || o != left->data) && (!right || o != right->data));
2155 
2156 }
2157 
2158 void
html_object_check_cut_lists(HTMLObject * self,HTMLObject * replacement,GList * left,GList * right)2159 html_object_check_cut_lists (HTMLObject *self,
2160                              HTMLObject *replacement,
2161                              GList *left,
2162                              GList *right)
2163 {
2164 	if (left && left->data == self)
2165 		left->data = replacement;
2166 	if (right && right->data == self)
2167 		right->data = replacement;
2168 }
2169 
2170 typedef struct {
2171 	HTMLInterval *i;
2172 	GString *buffer;
2173 	gboolean in;
2174 } tmpSelData;
2175 
2176 static void
select_object(HTMLObject * o,HTMLEngine * e,gpointer data)2177 select_object (HTMLObject *o,
2178                HTMLEngine *e,
2179                gpointer data)
2180 {
2181 	tmpSelData *d = (tmpSelData *) data;
2182 
2183 	if (o == d->i->from.object)
2184 		d->in = TRUE;
2185 	if (d->in)
2186 		html_object_select_range (o, e,
2187 					  html_interval_get_start (d->i, o),
2188 					  html_interval_get_length (d->i, o), FALSE);
2189 
2190 	if (o == d->i->to.object)
2191 		d->in = FALSE;
2192 }
2193 
2194 static void
unselect_object(HTMLObject * o,HTMLEngine * e,gpointer data)2195 unselect_object (HTMLObject *o,
2196                  HTMLEngine *e,
2197                  gpointer data)
2198 {
2199 	o->selected = FALSE;
2200 }
2201 
2202 gchar *
html_object_get_selection_string(HTMLObject * o,HTMLEngine * e)2203 html_object_get_selection_string (HTMLObject *o,
2204                                   HTMLEngine *e)
2205 {
2206 	HTMLObject *tail;
2207 	tmpSelData data;
2208 	gchar *string;
2209 
2210 	g_assert (o);
2211 
2212 	tail        = html_object_get_tail_leaf (o);
2213 	data.buffer = g_string_new (NULL);
2214 	data.in     = FALSE;
2215 	data.i      = html_interval_new (html_object_get_head_leaf (o), tail, 0, html_object_get_length (tail));
2216 
2217 	html_interval_forall (data.i, e, select_object, &data);
2218 	html_object_append_selection_string (o, data.buffer);
2219 	html_interval_forall (data.i, e, unselect_object, NULL);
2220 
2221 	html_interval_destroy (data.i);
2222 	string = data.buffer->str;
2223 	g_string_free (data.buffer, FALSE);
2224 
2225 	return string;
2226 }
2227 
2228 HTMLObject *
html_object_get_tail_leaf(HTMLObject * o)2229 html_object_get_tail_leaf (HTMLObject *o)
2230 {
2231 	HTMLObject *tail, *rv = o;
2232 
2233 	do {
2234 		tail = html_object_tail_not_slave (rv);
2235 		if (tail)
2236 			rv = tail;
2237 	} while (tail);
2238 
2239 	return rv;
2240 }
2241 
2242 HTMLObject *
html_object_get_head_leaf(HTMLObject * o)2243 html_object_get_head_leaf (HTMLObject *o)
2244 {
2245 	HTMLObject *head, *rv = o;
2246 
2247 	do {
2248 		head = html_object_head (rv);
2249 		if (head)
2250 			rv = head;
2251 	} while (head);
2252 
2253 	return rv;
2254 }
2255 
2256 HTMLObject *
html_object_nth_parent(HTMLObject * self,gint n)2257 html_object_nth_parent (HTMLObject *self,
2258                         gint n)
2259 {
2260 	while (self && n > 0) {
2261 		self = self->parent;
2262 		n--;
2263 	}
2264 
2265 	return self;
2266 }
2267 
2268 gint
html_object_get_parent_level(HTMLObject * self)2269 html_object_get_parent_level (HTMLObject *self)
2270 {
2271 	gint level = 0;
2272 
2273 	while (self) {
2274 		level++;
2275 		self = self->parent;
2276 	}
2277 
2278 	return level;
2279 }
2280 
2281 GList *
html_object_heads_list(HTMLObject * o)2282 html_object_heads_list (HTMLObject *o)
2283 {
2284 	GList *list = NULL;
2285 
2286 	g_return_val_if_fail (o, NULL);
2287 
2288 	while (o) {
2289 		list = g_list_append (list, o);
2290 		o = html_object_head (o);
2291 	}
2292 
2293 	return list;
2294 }
2295 
2296 GList *
html_object_tails_list(HTMLObject * o)2297 html_object_tails_list (HTMLObject *o)
2298 {
2299 	GList *list = NULL;
2300 
2301 	g_return_val_if_fail (o, NULL);
2302 
2303 	while (o) {
2304 		list = g_list_append (list, o);
2305 		o = html_object_tail_not_slave (o);
2306 	}
2307 
2308 	return list;
2309 }
2310 
2311 static void
merge_down(HTMLEngine * e,GList * left,GList * right)2312 merge_down (HTMLEngine *e,
2313             GList *left,
2314             GList *right)
2315 {
2316 	HTMLObject *lo;
2317 	HTMLObject *ro;
2318 
2319 	while (left && right) {
2320 		lo    = HTML_OBJECT (left->data);
2321 		ro    = HTML_OBJECT (right->data);
2322 		left  = left->next;
2323 		right = right->next;
2324 		if (!html_object_merge (lo, ro, e, &left, &right, NULL))
2325 			break;
2326 	}
2327 }
2328 
2329 void
html_object_merge_down(HTMLObject * o,HTMLObject * w,HTMLEngine * e)2330 html_object_merge_down (HTMLObject *o,
2331                         HTMLObject *w,
2332                         HTMLEngine *e)
2333 {
2334 	merge_down (e, html_object_tails_list (o), html_object_heads_list (w));
2335 }
2336 
2337 gboolean
html_object_is_parent(HTMLObject * parent,HTMLObject * child)2338 html_object_is_parent (HTMLObject *parent,
2339                        HTMLObject *child)
2340 {
2341 	g_assert (parent && child);
2342 
2343 	while (child) {
2344 		if (child->parent == parent)
2345 			return TRUE;
2346 		child = child->parent;
2347 	}
2348 
2349 	return FALSE;
2350 }
2351 
2352 gint
html_object_get_insert_level(HTMLObject * o)2353 html_object_get_insert_level (HTMLObject *o)
2354 {
2355 	switch (HTML_OBJECT_TYPE (o)) {
2356 	case HTML_TYPE_TABLECELL:
2357 	case HTML_TYPE_CLUEV: {
2358 		gint level = 3;
2359 
2360 		while (o && (HTML_IS_CLUEV (o) || HTML_IS_TABLE_CELL (o))
2361 		       && HTML_CLUE (o)->head && (HTML_IS_CLUEV (HTML_CLUE (o)->head) || HTML_IS_TABLE_CELL (HTML_CLUE (o)->head))) {
2362 			level++;
2363 			o = HTML_CLUE (o)->head;
2364 		}
2365 
2366 		return level;
2367 	}
2368 	case HTML_TYPE_CLUEFLOW:
2369 		return 2;
2370 	default:
2371 		return 1;
2372 	}
2373 }
2374 
2375 void
html_object_engine_translation(HTMLObject * o,HTMLEngine * e,gint * tx,gint * ty)2376 html_object_engine_translation (HTMLObject *o,
2377                                 HTMLEngine *e,
2378                                 gint *tx,
2379                                 gint *ty)
2380 {
2381 	HTMLObject *p;
2382 
2383 	*tx = 0;
2384 	*ty = 0;
2385 
2386 	for (p = o->parent; p != NULL && HTML_OBJECT_TYPE (p) != HTML_TYPE_IFRAME; p = p->parent) {
2387 		*tx += p->x;
2388 		*ty += p->y - p->ascent;
2389 	}
2390 
2391 	/* *tx += e->leftBorder; */
2392 	/* *ty += e->topBorder; */
2393 }
2394 
2395 gboolean
html_object_engine_intersection(HTMLObject * o,HTMLEngine * e,gint tx,gint ty,gint * x1,gint * y1,gint * x2,gint * y2)2396 html_object_engine_intersection (HTMLObject *o,
2397                                  HTMLEngine *e,
2398                                  gint tx,
2399                                  gint ty,
2400                                  gint *x1,
2401                                  gint *y1,
2402                                  gint *x2,
2403                                  gint *y2)
2404 {
2405 	*x1 = o->x + tx;
2406 	*y1 = o->y - o->ascent + ty;
2407 	*x2 = o->x + o->width + tx;
2408 	*y2 = o->y + o->descent + ty;
2409 
2410 	return html_engine_intersection (e, x1, y1, x2, y2);
2411 }
2412 
2413 void
html_object_add_to_changed(GList ** changed_objs,HTMLObject * o)2414 html_object_add_to_changed (GList **changed_objs,
2415                             HTMLObject *o)
2416 {
2417 	GList *l, *next;
2418 
2419 	if (!changed_objs || (*changed_objs && (*changed_objs)->data == o))
2420 		return;
2421 
2422 	for (l = *changed_objs; l; l = next) {
2423 		if (l->data == NULL) {
2424 			l = l->next;
2425 			next = l->next;
2426 			continue;
2427 		}
2428 		next = l->next;
2429 		if (html_object_is_parent (o, HTML_OBJECT (l->data))) {
2430 			*changed_objs = g_list_remove_link (*changed_objs, l);
2431 			g_list_free (l);
2432 		} else
2433 			break;
2434 	}
2435 
2436 	*changed_objs = g_list_prepend (*changed_objs, o);
2437 }
2438 
2439 gint
html_object_get_n_children(HTMLObject * self)2440 html_object_get_n_children (HTMLObject *self)
2441 {
2442 	return HO_CLASS (self)->get_n_children ? (* HO_CLASS (self)->get_n_children) (self) : 0;
2443 }
2444 
2445 HTMLObject *
html_object_get_child(HTMLObject * self,gint index)2446 html_object_get_child (HTMLObject *self,
2447                        gint index)
2448 {
2449 	return HO_CLASS (self)->get_child ? (* HO_CLASS (self)->get_child) (self, index) : NULL;
2450 }
2451 
2452 gint
html_object_get_child_index(HTMLObject * self,HTMLObject * child)2453 html_object_get_child_index (HTMLObject *self,
2454                              HTMLObject *child)
2455 {
2456 	return HO_CLASS (self)->get_child_index ? (* HO_CLASS (self)->get_child_index) (self, child) : -1;
2457 }
2458 
2459 HTMLClearType
html_object_get_clear(HTMLObject * self)2460 html_object_get_clear (HTMLObject *self)
2461 {
2462 	return (* HO_CLASS (self)->get_clear) (self);
2463 }
2464 
2465 static HTMLObject *
next_prev_cursor_object(HTMLObject * o,HTMLEngine * e,gint * offset,gboolean forward)2466 next_prev_cursor_object (HTMLObject *o,
2467                          HTMLEngine *e,
2468                          gint *offset,
2469                          gboolean forward)
2470 {
2471 	HTMLCursor cursor;
2472 	gboolean result;
2473 
2474 	html_cursor_init (&cursor, o, html_object_is_container (o) ? *offset : (forward ? html_object_get_length (o) : 0));
2475 
2476 	result = forward ? html_cursor_forward (&cursor, e) : html_cursor_backward (&cursor, e);
2477 	*offset = cursor.offset;
2478 
2479 	return result ? cursor.object : NULL;
2480 }
2481 
2482 HTMLObject *
html_object_next_cursor_object(HTMLObject * o,HTMLEngine * e,gint * offset)2483 html_object_next_cursor_object (HTMLObject *o,
2484                                 HTMLEngine *e,
2485                                 gint *offset)
2486 {
2487 	return next_prev_cursor_object (o, e, offset, TRUE);
2488 }
2489 
2490 HTMLObject *
html_object_prev_cursor_object(HTMLObject * o,HTMLEngine * e,gint * offset)2491 html_object_prev_cursor_object (HTMLObject *o,
2492                                 HTMLEngine *e,
2493                                 gint *offset)
2494 {
2495 	return next_prev_cursor_object (o, e, offset, FALSE);
2496 }
2497 
2498 HTMLObject *
html_object_next_cursor_leaf(HTMLObject * o,HTMLEngine * e)2499 html_object_next_cursor_leaf (HTMLObject *o,
2500                               HTMLEngine *e)
2501 {
2502 	gint offset = html_object_get_length (o);
2503 
2504 	o = html_object_next_cursor_object (o, e, &offset);
2505 	while (o && html_object_is_container (o))
2506 		o = html_object_next_cursor_object (o, e, &offset);
2507 
2508 	return o;
2509 }
2510 
2511 HTMLObject *
html_object_prev_cursor_leaf(HTMLObject * o,HTMLEngine * e)2512 html_object_prev_cursor_leaf (HTMLObject *o,
2513                               HTMLEngine *e)
2514 {
2515 	gint offset = html_object_get_length (o);
2516 
2517 	o = html_object_prev_cursor_object (o, e, &offset);
2518 	while (o && html_object_is_container (o))
2519 		o = html_object_prev_cursor_object (o, e, &offset);
2520 
2521 	return o;
2522 }
2523 
2524 HTMLDirection
html_object_get_direction(HTMLObject * o)2525 html_object_get_direction (HTMLObject *o)
2526 {
2527 	return (* HO_CLASS (o)->get_direction) (o);
2528 }
2529 
2530 const gchar *
html_object_get_id(HTMLObject * o)2531 html_object_get_id (HTMLObject *o)
2532 {
2533 	return o->id;
2534 }
2535 
2536 void
html_object_set_id(HTMLObject * o,const gchar * id)2537 html_object_set_id (HTMLObject *o,
2538                     const gchar *id)
2539 {
2540 	g_free (o->id);
2541 	o->id = g_strdup (id);
2542 }
2543 
2544 HTMLClueFlow *
html_object_get_flow(HTMLObject * o)2545 html_object_get_flow (HTMLObject *o)
2546 {
2547 	while (o && !HTML_IS_CLUEFLOW (o))
2548 		o = o->parent;
2549 
2550 	return HTML_CLUEFLOW (o);
2551 }
2552 
2553 gint
html_object_get_right_edge_offset(HTMLObject * o,HTMLPainter * painter,gint offset)2554 html_object_get_right_edge_offset (HTMLObject *o,
2555                                    HTMLPainter *painter,
2556                                    gint offset)
2557 {
2558 	return (* HO_CLASS (o)->get_right_edge_offset) (o, painter, offset);
2559 }
2560 
2561 gint
html_object_get_left_edge_offset(HTMLObject * o,HTMLPainter * painter,gint offset)2562 html_object_get_left_edge_offset (HTMLObject *o,
2563                                   HTMLPainter *painter,
2564                                   gint offset)
2565 {
2566 	return (* HO_CLASS (o)->get_left_edge_offset) (o, painter, offset);
2567 }
2568