source: trunk/third/gtkhtml/src/htmlobject.c @ 18136

Revision 18136, 44.1 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18135, which included commits to RCS files with non-trunk default branches.
Line 
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., 59 Temple Place - Suite 330,
21    Boston, MA 02111-1307, 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-save.h"
37#include "htmlframe.h"
38#include "htmlinterval.h"
39#include "htmlobject.h"
40#include "htmlpainter.h"
41#include "htmltable.h"
42#include "htmltext.h"
43#include "htmlrule.h"
44#include "htmltype.h"
45
46#include "gtkhtmldebug.h"
47
48
49HTMLObjectClass html_object_class;
50
51#define HO_CLASS(x) HTML_OBJECT_CLASS (HTML_OBJECT (x)->klass)
52
53
54/* HTMLObject virtual methods.  */
55
56static void
57destroy (HTMLObject *self)
58{
59#define GTKHTML_MEM_DEBUG 1
60#if GTKHTML_MEM_DEBUG
61        self->parent = HTML_OBJECT (0xdeadbeef);
62        self->next = HTML_OBJECT (0xdeadbeef);
63        self->prev = HTML_OBJECT (0xdeadbeef);
64#else
65        self->next = NULL;
66        self->prev = NULL;
67#endif
68        g_datalist_clear (&self->object_data);
69       
70        if (self->redraw_pending) {
71                self->free_pending = TRUE;
72        } else {
73                g_free (self);
74        }
75}
76
77static void
78copy (HTMLObject *self,
79      HTMLObject *dest)
80{
81        dest->klass = self->klass;
82        dest->parent = NULL;
83        dest->prev = NULL;
84        dest->next = NULL;
85        dest->x = 0;
86        dest->y = 0;
87        dest->ascent = self->ascent;
88        dest->descent = self->descent;
89        dest->width = self->width;
90        dest->min_width = self->min_width;
91        dest->max_width = self->max_width;
92        dest->pref_width = self->pref_width;
93        dest->percent = self->percent;
94        dest->flags = self->flags;
95        dest->redraw_pending = FALSE;
96        dest->selected = self->selected;
97        dest->free_pending = FALSE;
98        dest->change = self->change;
99
100        g_datalist_init (&dest->object_data);
101        html_object_copy_data_from_object (dest, self);
102}
103
104static HTMLObject *
105op_copy (HTMLObject *self, HTMLObject *parent, HTMLEngine *e, GList *from, GList *to, guint *len)
106{
107        if ((!from || GPOINTER_TO_INT (from->data) == 0)
108            && (!to || GPOINTER_TO_INT (to->data) == html_object_get_length (self))) {
109                *len += html_object_get_recursive_length (self);
110
111                return html_object_dup (self);
112        } else
113                return html_engine_new_text_empty (e);
114}
115
116static HTMLObject *
117op_cut (HTMLObject *self, HTMLEngine *e, GList *from, GList *to, GList *left, GList *right, guint *len)
118{
119        if ((!from || GPOINTER_TO_INT (from->data) == 0)
120            && (!to || GPOINTER_TO_INT (to->data) == html_object_get_length (self))) {
121                if (!html_object_could_remove_whole (self, from, to, left, right)) {
122                        HTMLObject *empty = html_engine_new_text_empty (e);
123
124                        if (e->cursor->object == self)
125                                e->cursor->object = empty;
126                        html_clue_append_after (HTML_CLUE (self->parent), empty, self);
127                        html_object_change_set (empty, HTML_CHANGE_ALL_CALC);
128                        html_object_check_cut_lists (self, empty, left, right);
129                } else
130                        html_object_move_cursor_before_remove (self, e);
131
132                html_object_change_set   (self,  HTML_CHANGE_ALL_CALC);
133                html_object_remove_child (self->parent, self);
134                *len += html_object_get_recursive_length (self);
135
136                return self;
137        } else
138                return html_engine_new_text_empty (e);
139}
140
141static gboolean
142merge (HTMLObject *self, HTMLObject *with, HTMLEngine *e, GList **left, GList **right, HTMLCursor *cursor)
143{
144        return FALSE;
145}
146
147static void
148remove_child (HTMLObject *self, HTMLObject *child)
149{
150        g_warning ("REMOVE CHILD unimplemented for ");
151        gtk_html_debug_dump_object_type (self);
152        g_assert_not_reached ();
153}
154
155static void
156split (HTMLObject *self, HTMLEngine *e, HTMLObject *child, gint offset, gint level, GList **left, GList **right)
157{
158        if (child || (offset && html_object_get_length (self) != offset)) {
159                g_warning ("don't know how to SPLIT ");
160                gtk_html_debug_dump_object_type (self);
161                return;
162        }
163
164        if (offset) {
165                if (!self->next) {
166                        html_clue_append (HTML_CLUE (self->parent), html_engine_new_text_empty (e));
167                }
168                *left  = g_list_prepend (*left,  self);
169                *right = g_list_prepend (*right, self->next);
170        } else {
171                if (!self->prev) {
172                        e->cursor->object = html_engine_new_text_empty (e);
173                        e->cursor->offset = 0;
174                        html_clue_prepend (HTML_CLUE (self->parent), e->cursor->object);
175                }
176                *left  = g_list_prepend (*left,  self->prev);
177                *right = g_list_prepend (*right, self);
178        }
179        level--;
180
181        if (level && self->parent)
182                html_object_split (self->parent, e, offset ? self->next : self, 0, level, left, right);
183}
184
185static void
186draw (HTMLObject *o,
187      HTMLPainter *p,
188      gint x, gint y,
189      gint width, gint height,
190      gint tx, gint ty)
191{
192        /* Do nothing by default.  We don't know how to paint ourselves.  */
193}
194
195static void
196draw_background (HTMLObject *self,
197                 HTMLPainter *p,
198                 gint x, gint y,
199                 gint width, gint height,
200                 gint tx, gint ty)
201{
202        /* By default, objects are transparent so they simply forward
203           this to the parent.  */
204        if (self->parent != NULL) {
205                html_object_draw_background (self->parent, p,
206                                             x + self->parent->x,
207                                             y + self->parent->y - self->parent->ascent,
208                                             width, height,
209                                             tx - self->parent->x,
210                                             ty - self->parent->y + self->parent->ascent);
211        } else {
212                /* FIXME this should draw the default background somehow.  */
213        }
214}
215
216static gboolean
217is_transparent (HTMLObject *self)
218{
219        return TRUE;
220}
221
222static HTMLFitType
223fit_line (HTMLObject *o,
224          HTMLPainter *painter,
225          gboolean start_of_line,
226          gboolean first_run,
227          gboolean next_to_floating,
228          gint width_left)
229{
230        html_object_calc_size (o, painter, FALSE);
231        return (o->width <= width_left || (first_run && !next_to_floating)) ? HTML_FIT_COMPLETE : HTML_FIT_NONE;
232}
233
234static gboolean
235calc_size (HTMLObject *o, HTMLPainter *painter, GList **changed_objs)
236{
237        return FALSE;
238}
239
240static gint
241calc_min_width (HTMLObject *o, HTMLPainter *painter)
242{
243        html_object_calc_size (o, painter, FALSE);
244        return o->width;
245}
246
247static gint
248calc_preferred_width (HTMLObject *o, HTMLPainter *painter)
249{
250        html_object_calc_size (o, painter, FALSE);
251        return o->width;
252}
253       
254static void
255set_max_width (HTMLObject *o, HTMLPainter *painter, gint max_width)
256{
257        o->max_width = max_width;
258}
259
260static void
261set_max_height (HTMLObject *o, HTMLPainter *painter, gint max_height)
262{
263}
264
265static gint
266get_left_margin (HTMLObject *self, HTMLPainter *painter, gint y, gboolean with_aligned)
267{
268        return 0;
269}
270
271static gint
272get_right_margin (HTMLObject *self, HTMLPainter *painter, gint y, gboolean with_aligned)
273{
274        return MAX (self->max_width, self->width);
275}
276
277static void
278set_painter (HTMLObject *o, HTMLPainter *painter)
279{
280}
281
282static void
283reset (HTMLObject *o)
284{
285        /* o->width = 0;
286           o->ascent = 0;
287           o->descent = 0; */
288}
289
290static const gchar *
291get_url (HTMLObject *o)
292{
293        return NULL;
294}
295
296static const gchar *
297get_target (HTMLObject *o)
298{
299        return NULL;
300}
301
302static const gchar *
303get_src (HTMLObject *o)
304{
305        return NULL;
306}
307
308static HTMLAnchor *
309find_anchor (HTMLObject *o,
310             const gchar *name,
311             gint *x, gint *y)
312{
313        return NULL;
314}
315
316static void
317set_bg_color (HTMLObject *o,
318              GdkColor *color)
319{
320}
321
322static GdkColor *
323get_bg_color (HTMLObject *o,
324              HTMLPainter *p)
325{
326       
327        return o->parent
328                ? html_object_get_bg_color (o->parent, p)
329                : &((html_colorset_get_color (p->color_set, HTMLBgColor))->color);
330}
331
332static HTMLObject*
333check_point (HTMLObject *self,
334             HTMLPainter *painter,
335             gint x, gint y,
336             guint *offset_return,
337             gboolean for_cursor)
338{
339        if (x >= self->x
340            && x < self->x + self->width
341            && y >= self->y - self->ascent
342            && y < self->y + self->descent) {
343                if (offset_return != NULL)
344                        *offset_return = 0;
345                return self;
346        }
347   
348        return NULL;
349}
350
351static gboolean
352relayout (HTMLObject *self,
353          HTMLEngine *engine,
354          HTMLObject *child)
355{
356        /* FIXME int types of this stuff might change in `htmlobject.h',
357           remember to sync.  */
358        guint prev_width;
359        guint prev_ascent, prev_descent;
360        gboolean changed;
361
362        if (html_engine_frozen (engine))
363                return FALSE;
364
365        prev_width = self->width;
366        prev_ascent = self->ascent;
367        prev_descent = self->descent;
368
369        /* Notice that this will reset ascent and descent which we
370           need afterwards.  Yeah, yuck, bleargh.  */
371        html_object_reset (self);
372
373        /* Crappy hack to make crappy htmlclueflow.c happy.  */
374        if (self->y < self->ascent + self->descent) {
375                g_warning ("htmlobject.c:relayout -- Eeek! This should not happen!  "
376                           "Y value < height of object!\n");
377                self->y = 0;
378        } else {
379                self->y -= prev_ascent + prev_descent;
380        }
381
382        changed = html_object_calc_size (self, engine->painter, FALSE);
383
384        if (prev_width == self->width
385            && prev_ascent == self->ascent
386            && prev_descent == self->descent) {
387                gtk_html_debug_log (engine->widget,
388                                    "relayout: %s %p did not change.\n",
389                                    html_type_name (HTML_OBJECT_TYPE (self)),
390                                    self);
391                if (changed)
392                        html_engine_queue_draw (engine, self);
393
394                return FALSE;
395        }
396       
397        gtk_html_debug_log (engine->widget, "relayout: %s %p changed.\n",
398                            html_type_name (HTML_OBJECT_TYPE (self)), self);
399
400        if (self->parent == NULL) {
401                /* FIXME resize the widget, e.g. scrollbars and such.  */
402                html_engine_queue_draw (engine, self);
403
404                /* FIXME extreme ugliness.  */
405                self->x = 0;
406                self->y = self->ascent;
407        } else {
408                /* Relayout our parent starting from us.  */
409                if (! html_object_relayout (self->parent, engine, self))
410                        html_engine_queue_draw (engine, self);
411        }
412
413        /* If the object has shrunk, we have to clean the areas around
414           it so that we don't leave garbage on the screen.  FIXME:
415           this wastes some time if there is an object on the right of
416           or under this one.  */
417
418        if (prev_ascent + prev_descent > self->ascent + self->descent)
419                html_engine_queue_clear (engine,
420                                         self->x,
421                                         self->y + self->descent,
422                                         self->width,
423                                         (prev_ascent + prev_descent
424                                          - (self->ascent + self->descent)));
425
426        if (prev_width > self->width)
427                html_engine_queue_clear (engine,
428                                         self->x + self->width,
429                                         self->y - self->ascent,
430                                         prev_width - self->width,
431                                         self->ascent + self->descent);
432
433        return TRUE;
434}
435
436static HTMLVAlignType
437get_valign (HTMLObject *self)
438{
439        return HTML_VALIGN_BOTTOM;
440}
441
442static gboolean
443accepts_cursor (HTMLObject *self)
444{
445        return FALSE;
446}
447
448static void
449get_cursor (HTMLObject *self,
450            HTMLPainter *painter,
451            guint offset,
452            gint *x1, gint *y1,
453            gint *x2, gint *y2)
454{
455        html_object_get_cursor_base (self, painter, offset, x2, y2);
456
457        *x1 = *x2;
458        *y1 = *y2 - self->ascent;
459        *y2 += self->descent - 1;
460}
461
462static void
463get_cursor_base (HTMLObject *self,
464                 HTMLPainter *painter,
465                 guint offset,
466                 gint *x, gint *y)
467{
468        html_object_calc_abs_position (self, x, y);
469
470        if (offset > 0)
471                *x += self->width;
472}
473
474static guint
475get_length (HTMLObject *self)
476{
477        return 1;
478}
479
480static guint
481get_line_length (HTMLObject *self, HTMLPainter *p, gint line_offset)
482{
483        return html_object_get_length (self);
484}
485
486static guint
487get_recursive_length (HTMLObject *self)
488{
489        return html_object_get_length (self);
490}
491
492static gboolean
493select_range (HTMLObject *self,
494              HTMLEngine *engine,
495              guint start,
496              gint length,
497              gboolean queue_draw)
498{
499        gboolean selected;
500        gboolean changed;
501
502        selected = length > 0 || (length == -1 && start < html_object_get_length (self)) ? TRUE : FALSE;
503        changed  = (! selected && self->selected) || (selected && ! self->selected) ? TRUE : FALSE;
504
505        self->selected = selected;
506
507        return changed;
508}
509
510static void
511append_selection_string (HTMLObject *self,
512                         GString *buffer)
513{
514}
515
516static void
517forall (HTMLObject *self,
518        HTMLEngine *e,
519        HTMLObjectForallFunc func,
520        gpointer data)
521{
522        (* func) (self, e, data);
523}
524
525static HTMLEngine *
526get_engine (HTMLObject *self,
527            HTMLEngine *e)
528{
529        return e;
530}
531
532static gboolean
533is_container (HTMLObject *self)
534{
535        return FALSE;
536}
537
538static gboolean
539save (HTMLObject *self,
540      HTMLEngineSaveState *state)
541{
542        return TRUE;
543}
544
545static gboolean
546save_plain (HTMLObject *self,
547            HTMLEngineSaveState *state,
548            gint requested_width)
549{
550        return TRUE;
551}
552
553static gint
554check_page_split (HTMLObject *self,
555                  gint y)
556{
557        return 0;
558}
559
560static gboolean
561search (HTMLObject *self, HTMLSearch *info)
562{
563        /* not found by default */
564        return FALSE;
565}
566
567static HTMLObject *
568next (HTMLObject *self, HTMLObject *child)
569{
570        return child->next;
571}
572
573static HTMLObject *
574prev (HTMLObject *self, HTMLObject *child)
575{
576        return child->prev;
577}
578
579static HTMLObject *
580head (HTMLObject *self)
581{
582        return NULL;
583}
584
585static HTMLObject *
586tail (HTMLObject *self)
587{
588        return NULL;
589}
590
591static HTMLClearType
592get_clear (HTMLObject *self)
593{
594        return HTML_CLEAR_NONE;
595}
596
597/* Class initialization.  */
598
599void
600html_object_type_init (void)
601{
602        html_object_class_init (&html_object_class, HTML_TYPE_OBJECT, sizeof (HTMLObject));
603}
604
605void
606html_object_class_init (HTMLObjectClass *klass,
607                        HTMLType type,
608                        guint object_size)
609{
610        g_return_if_fail (klass != NULL);
611
612        /* Set type.  */
613        klass->type = type;
614        klass->object_size = object_size;
615
616        /* Install virtual methods.  */
617        klass->destroy = destroy;
618        klass->copy = copy;
619        klass->op_copy = op_copy;
620        klass->op_cut = op_cut;
621        klass->merge = merge;
622        klass->remove_child = remove_child;
623        klass->split = split;
624        klass->draw = draw;
625        klass->draw_background = draw_background;
626        klass->is_transparent = is_transparent;
627        klass->fit_line = fit_line;
628        klass->calc_size = calc_size;
629        klass->set_max_width = set_max_width;
630        klass->set_max_height = set_max_height;
631        klass->get_left_margin = get_left_margin;
632        klass->get_right_margin = get_right_margin;
633        klass->set_painter = set_painter;
634        klass->reset = reset;
635        klass->calc_min_width = calc_min_width;
636        klass->calc_preferred_width = calc_preferred_width;
637        klass->get_url = get_url;
638        klass->get_target = get_target;
639        klass->get_src = get_src;
640        klass->find_anchor = find_anchor;
641        klass->set_link = NULL;
642        klass->set_bg_color = set_bg_color;
643        klass->get_bg_color = get_bg_color;
644        klass->check_point = check_point;
645        klass->relayout = relayout;
646        klass->get_valign = get_valign;
647        klass->accepts_cursor = accepts_cursor;
648        klass->get_cursor = get_cursor;
649        klass->get_cursor_base = get_cursor_base;
650        klass->select_range = select_range;
651        klass->append_selection_string = append_selection_string;
652        klass->forall = forall;
653        klass->is_container = is_container;
654        klass->save = save;
655        klass->save_plain = save_plain;
656        klass->check_page_split = check_page_split;
657        klass->search = search;
658        klass->search_next = search;
659        klass->get_length = get_length;
660        klass->get_line_length = get_line_length;
661        klass->get_recursive_length = get_recursive_length;
662        klass->next = next;
663        klass->prev = prev;
664        klass->head = head;
665        klass->tail = tail;
666        klass->get_engine = get_engine;
667        klass->get_clear = get_clear;
668}
669
670void
671html_object_init (HTMLObject *o,
672                  HTMLObjectClass *klass)
673{
674        o->klass = klass;
675
676        o->parent = NULL;
677        o->prev = NULL;
678        o->next = NULL;
679
680        /* we don't have any info cached in the beginning */
681        o->change = HTML_CHANGE_ALL;
682
683        o->x = 0;
684        o->y = 0;
685
686        o->ascent = 0;
687        o->descent = 0;
688
689        o->width = 0;
690        o->max_width = 0;
691        o->min_width = 0;
692        o->pref_width = 0;
693        o->percent = 0;
694
695        o->flags = HTML_OBJECT_FLAG_FIXEDWIDTH; /* FIXME Why? */
696
697        o->redraw_pending = FALSE;
698        o->free_pending = FALSE;
699        o->selected = FALSE;
700
701        g_datalist_init (&o->object_data);
702}
703
704HTMLObject *
705html_object_new (HTMLObject *parent)
706{
707        HTMLObject *o;
708       
709        o = g_new0 (HTMLObject, 1);
710        html_object_init (o, &html_object_class);
711
712        return o;
713}
714
715
716/* Object duplication.  */
717
718HTMLObject *
719html_object_dup (HTMLObject *object)
720{
721        HTMLObject *new;
722
723        g_return_val_if_fail (object != NULL, NULL);
724
725        new = g_malloc (object->klass->object_size);
726        html_object_copy (object, new);
727
728        return new;
729}
730
731HTMLObject *
732html_object_op_copy (HTMLObject *self, HTMLObject *parent, HTMLEngine *e, GList *from, GList *to, guint *len)
733{
734        return (* HO_CLASS (self)->op_copy) (self, parent, e, from, to, len);
735}
736
737HTMLObject *
738html_object_op_cut (HTMLObject *self, HTMLEngine *e, GList *from, GList *to, GList *left, GList *right, guint *len)
739{
740        return (* HO_CLASS (self)->op_cut) (self, e, from, to, left, right, len);
741}
742
743gboolean
744html_object_merge (HTMLObject *self, HTMLObject *with, HTMLEngine *e, GList **left, GList **right, HTMLCursor *cursor)
745{
746        if ((HTML_OBJECT_TYPE (self) == HTML_OBJECT_TYPE (with)
747             /* FIXME */
748             || (HTML_OBJECT_TYPE (self) == HTML_TYPE_TABLECELL && HTML_OBJECT_TYPE (with) == HTML_TYPE_CLUEV)
749             || (HTML_OBJECT_TYPE (with) == HTML_TYPE_TABLECELL && HTML_OBJECT_TYPE (self) == HTML_TYPE_CLUEV))
750            && (* HO_CLASS (self)->merge) (self, with, e, left, right, cursor)) {
751                if (with->parent)
752                        html_object_remove_child (with->parent, with);
753                html_object_destroy (with);
754                return TRUE;
755        }
756        return FALSE;
757}
758
759void
760html_object_remove_child (HTMLObject *self, HTMLObject *child)
761{
762        g_assert (self);
763        g_assert (child);
764
765        (* HO_CLASS (self)->remove_child) (self, child);
766}
767
768void
769html_object_split (HTMLObject *self, HTMLEngine *e, HTMLObject *child, gint offset, gint level,
770                   GList **left, GList **right)
771{
772        g_assert (self);
773
774        (* HO_CLASS (self)->split) (self, e, child, offset, level, left, right);
775}
776
777
778void
779html_object_set_parent (HTMLObject *o, HTMLObject *parent)
780{
781        o->parent = parent;
782}
783
784void
785html_object_calc_abs_position (HTMLObject *o,
786                               gint *x_return, gint *y_return)
787{
788        HTMLObject *p;
789
790        g_return_if_fail (o != NULL);
791
792        *x_return = o->x;
793        *y_return = o->y;
794
795        for (p = o->parent; p != NULL; p = p->parent) {
796                *x_return += p->x;
797                *y_return += p->y - p->ascent;
798                if (html_object_is_frame (p)) {
799                        *x_return += GTK_HTML (HTML_FRAME (p)->html)->engine->leftBorder;
800                        *y_return += GTK_HTML (HTML_FRAME (p)->html)->engine->topBorder;
801                }
802        }
803}
804
805void
806html_object_calc_intersection (HTMLObject *o, ArtIRect *intersection, gint x, gint y, gint width, gint height)
807{
808        ArtIRect clip, area;
809       
810        area.x0 = o->x;
811        area.x1 = o->x + o->width;
812        area.y0 = o->y - o->ascent;
813        area.y1 = o->y + o->descent;
814
815        clip.x0 = x;
816        clip.x1 = x + width;
817        clip.y0 = y;
818        clip.y1 = y + height;
819
820        art_irect_intersect (intersection, &clip, &area);
821}
822
823
824/* Virtual methods.  */
825
826void
827html_object_destroy (HTMLObject *self)
828{
829        (* HO_CLASS (self)->destroy) (self);
830}
831
832void
833html_object_copy (HTMLObject *self,
834                  HTMLObject *dest)
835{
836        (* HO_CLASS (self)->copy) (self, dest);
837}
838
839void
840html_object_draw (HTMLObject *o,
841                  HTMLPainter *p,
842                  gint x, gint y,
843                  gint width, gint height,
844                  gint tx, gint ty)
845{
846        (* HO_CLASS (o)->draw) (o, p, x, y, width, height, tx, ty);
847}
848
849void
850html_object_draw_background (HTMLObject *o,
851                             HTMLPainter *p,
852                             gint x, gint y,
853                             gint width, gint height,
854                             gint tx, gint ty)
855{
856        (* HO_CLASS (o)->draw_background) (o, p, x, y, width, height, tx, ty);
857}
858
859gboolean
860html_object_is_transparent (HTMLObject *self)
861{
862        g_return_val_if_fail (self != NULL, TRUE);
863
864        return (* HO_CLASS (self)->is_transparent) (self);
865}
866
867HTMLFitType
868html_object_fit_line (HTMLObject *o,
869                      HTMLPainter *painter,
870                      gboolean start_of_line,
871                      gboolean first_run,
872                      gboolean next_to_floating,
873                      gint width_left)
874{
875        return (* HO_CLASS (o)->fit_line) (o, painter, start_of_line, first_run, next_to_floating, width_left);
876}
877
878gboolean
879html_object_calc_size (HTMLObject *o, HTMLPainter *painter, GList **changed_objs)
880{
881        gboolean rv;
882
883        rv = (* HO_CLASS (o)->calc_size) (o, painter, changed_objs);
884        o->change &= ~HTML_CHANGE_SIZE;
885
886        return rv;
887}
888
889void
890html_object_set_max_width (HTMLObject *o, HTMLPainter *painter, gint max_width)
891{
892        (* HO_CLASS (o)->set_max_width) (o, painter, max_width);
893}
894
895void
896html_object_set_max_height (HTMLObject *o, HTMLPainter *painter, gint max_height)
897{
898        (* HO_CLASS (o)->set_max_height) (o, painter, max_height);
899}
900
901gint
902html_object_get_left_margin (HTMLObject *self, HTMLPainter *painter, gint y, gboolean with_aligned)
903{
904        return (* HO_CLASS (self)->get_left_margin) (self, painter, y, with_aligned);
905}
906
907gint
908html_object_get_right_margin (HTMLObject *self, HTMLPainter *painter, gint y, gboolean with_aligned)
909{
910        return (* HO_CLASS (self)->get_right_margin) (self, painter, y, with_aligned);
911}
912
913static void
914set_painter_forall (HTMLObject *o, HTMLEngine *e, gpointer data)
915{
916        (* HO_CLASS (o)->set_painter) (o, HTML_PAINTER (data));
917}
918
919void
920html_object_set_painter (HTMLObject *o, HTMLPainter *painter)
921{
922        html_object_forall (o, NULL, set_painter_forall, painter);
923}
924
925void
926html_object_reset (HTMLObject *o)
927{
928        (* HO_CLASS (o)->reset) (o);
929}
930
931gint
932html_object_calc_min_width (HTMLObject *o,
933                            HTMLPainter *painter)
934{
935        if (o->change & HTML_CHANGE_MIN_WIDTH) {
936                o->min_width = (* HO_CLASS (o)->calc_min_width) (o, painter);
937                o->change &= ~HTML_CHANGE_MIN_WIDTH;
938        }
939        return o->min_width;
940}
941
942gint
943html_object_calc_preferred_width (HTMLObject *o,
944                                  HTMLPainter *painter)
945{
946        if (o->change & HTML_CHANGE_PREF_WIDTH) {
947                o->pref_width = (* HO_CLASS (o)->calc_preferred_width) (o, painter);
948                o->change &= ~HTML_CHANGE_PREF_WIDTH;
949        }
950        return o->pref_width;
951}
952
953#if 0
954gint
955html_object_get_uris (HTMLObject *o, char **link, char **target, char **src)
956{
957        return TRUE;
958}
959#endif
960
961const gchar *
962html_object_get_url (HTMLObject *o)
963{
964        return (* HO_CLASS (o)->get_url) (o);
965}
966
967const gchar *
968html_object_get_target (HTMLObject *o)
969{
970        return (* HO_CLASS (o)->get_target) (o);
971}
972
973gchar *
974html_object_get_complete_url (HTMLObject *o)
975{
976        const gchar *url, *target;
977
978        url = html_object_get_url (o);
979        target = html_object_get_target (o);
980        return url || target ? g_strconcat (url ? url : "#", url ? (target && *target ? "#" : NULL) : target,
981                                              url ? target : NULL, NULL) : NULL;
982}
983
984const gchar *
985html_object_get_src (HTMLObject *o)
986{
987        return (* HO_CLASS (o)->get_src) (o);
988}
989
990HTMLAnchor *
991html_object_find_anchor (HTMLObject *o,
992                         const gchar *name,
993                         gint *x, gint *y)
994{
995        return (* HO_CLASS (o)->find_anchor) (o, name, x, y);
996}
997
998void
999html_object_set_bg_color (HTMLObject *o, GdkColor *color)
1000{
1001        (* HO_CLASS (o)->set_bg_color) (o, color);
1002}
1003
1004GdkColor *
1005html_object_get_bg_color (HTMLObject *o, HTMLPainter *p)
1006{
1007        return (* HO_CLASS (o)->get_bg_color) (o, p);
1008}
1009
1010HTMLObject *
1011html_object_check_point (HTMLObject *self,
1012                         HTMLPainter *painter,
1013                         gint x, gint y,
1014                         guint *offset_return,
1015                         gboolean for_cursor)
1016{
1017        if (self->width == 0 || self->ascent + self->descent == 0)
1018                return NULL;
1019
1020        return (* HO_CLASS (self)->check_point) (self, painter, x, y, offset_return, for_cursor);
1021}
1022
1023gboolean
1024html_object_relayout (HTMLObject *self,
1025                      HTMLEngine *engine,
1026                      HTMLObject *child)
1027{
1028        g_return_val_if_fail (self != NULL, TRUE);
1029        return (* HO_CLASS (self)->relayout) (self, engine, child);
1030}
1031
1032HTMLVAlignType
1033html_object_get_valign (HTMLObject *self)
1034{
1035        g_return_val_if_fail (self != NULL, HTML_VALIGN_BOTTOM);
1036
1037        return (* HO_CLASS (self)->get_valign) (self);
1038}
1039
1040gboolean
1041html_object_accepts_cursor (HTMLObject *self)
1042{
1043        return (* HO_CLASS (self)->accepts_cursor) (self);
1044}
1045
1046/* Warning: `calc_size()' must have been called on `self' before this so that
1047   this works correctly.  */
1048void
1049html_object_get_cursor (HTMLObject *self,
1050                        HTMLPainter *painter,
1051                        guint offset,
1052                        gint *x1, gint *y1,
1053                        gint *x2, gint *y2)
1054{
1055        (* HO_CLASS (self)->get_cursor) (self, painter, offset, x1, y1, x2, y2);
1056        if (!html_object_is_text (self) && *y2 - *y1 < 10) {
1057                gint missing = 10 - (*y2 - *y1);
1058
1059                *y1 -= (missing >> 1) + ((missing >> 1) & 1);
1060                *y2 += missing >> 1;
1061        }
1062}
1063
1064/* Warning: `calc_size()' must have been called on `self' before this so that
1065   this works correctly.  */
1066void
1067html_object_get_cursor_base (HTMLObject *self,
1068                             HTMLPainter *painter,
1069                             guint offset,
1070                             gint *x, gint *y)
1071{
1072        (* HO_CLASS (self)->get_cursor_base) (self, painter, offset, x, y);
1073}
1074
1075
1076gboolean
1077html_object_select_range (HTMLObject *self,
1078                          HTMLEngine *engine,
1079                          guint start,
1080                          gint length,
1081                          gboolean queue_draw)
1082{
1083        return (* HO_CLASS (self)->select_range) (self, engine, start, length, queue_draw);
1084}
1085
1086void
1087html_object_append_selection_string (HTMLObject *self,
1088                                     GString *buffer)
1089{
1090        g_return_if_fail (self != NULL);
1091        g_return_if_fail (buffer != NULL);
1092
1093        (* HO_CLASS (self)->append_selection_string) (self, buffer);
1094}
1095
1096HTMLEngine *
1097html_object_get_engine (HTMLObject *self, HTMLEngine *e)
1098{
1099        return (* HO_CLASS (self)->get_engine) (self, e);
1100}
1101
1102HTMLEngine *
1103html_object_engine (HTMLObject *o, HTMLEngine *e)
1104{
1105        while (o) {
1106                e = html_object_get_engine (o, e);
1107                if (html_object_is_frame (o))
1108                        break;
1109                o = o->parent;
1110        }
1111
1112        return e;
1113}
1114
1115void
1116html_object_forall (HTMLObject *self,
1117                    HTMLEngine *e,
1118                    HTMLObjectForallFunc func,
1119                    gpointer data)
1120{
1121        (* HO_CLASS (self)->forall) (self, e, func, data);
1122}
1123
1124/* Ugly.  We should have an `is_a' implementation.  */
1125gboolean
1126html_object_is_container (HTMLObject *self)
1127{
1128        return (* HO_CLASS (self)->is_container) (self);
1129}
1130
1131
1132/* Ugly.  We should have an `is_a' implementation.  */
1133
1134gboolean
1135html_object_is_text (HTMLObject *object)
1136{
1137        HTMLType type;
1138
1139        g_return_val_if_fail (object != NULL, FALSE);
1140
1141        type = HTML_OBJECT_TYPE (object);
1142
1143        return (type == HTML_TYPE_TEXT || type == HTML_TYPE_LINKTEXT);
1144}
1145
1146gboolean
1147html_object_is_clue (HTMLObject *object)
1148{
1149        HTMLType type;
1150
1151        g_return_val_if_fail (object != NULL, FALSE);
1152
1153        type = HTML_OBJECT_TYPE (object);
1154
1155        return (type == HTML_TYPE_CLUE || type == HTML_TYPE_CLUEV || type == HTML_TYPE_TABLECELL
1156                || type == HTML_TYPE_CLUEFLOW || type == HTML_TYPE_CLUEALIGNED);
1157}
1158
1159HTMLObject *
1160html_object_next_not_type (HTMLObject *object, HTMLType t)
1161{
1162        HTMLObject *p;
1163
1164        g_return_val_if_fail (object != NULL, NULL);
1165        g_return_val_if_fail (object->parent, NULL);
1166
1167        p = html_object_next (object->parent, object);
1168        while (p && HTML_OBJECT_TYPE (p) == t)
1169                p = html_object_next (p->parent, p);
1170
1171        return p;
1172}
1173
1174HTMLObject *
1175html_object_prev_not_type (HTMLObject *object, HTMLType t)
1176{
1177        HTMLObject *p;
1178
1179        g_return_val_if_fail (object != NULL, NULL);
1180        g_return_val_if_fail (object->parent, NULL);
1181
1182        p = html_object_prev (object->parent, object);
1183        while (p && HTML_OBJECT_TYPE (p) == t)
1184                p = html_object_prev (p->parent, p);
1185
1186        return p;
1187}
1188
1189HTMLObject *
1190html_object_next_not_slave (HTMLObject *object)
1191{
1192        return html_object_next_not_type (object, HTML_TYPE_TEXTSLAVE);
1193}
1194
1195HTMLObject *
1196html_object_prev_not_slave (HTMLObject *object)
1197{
1198        return html_object_prev_not_type (object, HTML_TYPE_TEXTSLAVE);
1199}
1200
1201
1202gboolean
1203html_object_save (HTMLObject *self,
1204                  HTMLEngineSaveState *state)
1205{
1206        return (* HO_CLASS (self)->save) (self, state);
1207}
1208
1209gboolean
1210html_object_save_plain (HTMLObject *self,
1211                        HTMLEngineSaveState *state,
1212                        gint requested_width)
1213{
1214        return (* HO_CLASS (self)->save_plain) (self, state, requested_width);
1215}
1216
1217gint
1218html_object_check_page_split  (HTMLObject *self,
1219                               gint y)
1220{
1221        g_return_val_if_fail (self != NULL, 0);
1222
1223        return (* HO_CLASS (self)->check_page_split) (self, y);
1224}
1225
1226void
1227html_object_change_set (HTMLObject *self, HTMLChangeFlags f)
1228{
1229        HTMLObject *obj = self;
1230
1231        g_assert (self != NULL);
1232
1233        if (f != HTML_CHANGE_NONE) {
1234                while (obj) {
1235                        obj->change |= f;
1236                        obj = obj->parent;
1237                }
1238        }
1239}
1240
1241static void
1242change (HTMLObject *o, HTMLEngine *e, gpointer data)
1243{
1244        o->change |= GPOINTER_TO_INT (data);
1245}
1246
1247void
1248html_object_change_set_down (HTMLObject *self, HTMLChangeFlags f)
1249{
1250        html_object_forall (self, NULL, (HTMLObjectForallFunc) change, GINT_TO_POINTER (f));
1251}
1252
1253gboolean
1254html_object_search (HTMLObject *self, HTMLSearch *info)
1255{
1256        return (* HO_CLASS (self)->search) (self, info);
1257}
1258
1259gboolean
1260html_object_search_next (HTMLObject *self, HTMLSearch *info)
1261{
1262        return (* HO_CLASS (self)->search_next) (self, info);
1263}
1264
1265HTMLObject *
1266html_object_set_link (HTMLObject *self,
1267                      HTMLColor *color,
1268                      const gchar *url,
1269                      const gchar *target)
1270{
1271        return (HO_CLASS (self)->set_link) ? (* HO_CLASS (self)->set_link) (self, color, url, target) : NULL;
1272}
1273
1274HTMLObject *
1275html_object_remove_link (HTMLObject *self,
1276                         HTMLColor *color)
1277{
1278        return (HO_CLASS (self)->set_link) ? (* HO_CLASS (self)->set_link) (self, color, NULL, NULL) : NULL;
1279}
1280
1281guint
1282html_object_get_length (HTMLObject *self)
1283{
1284        return (* HO_CLASS (self)->get_length) (self);
1285}
1286
1287guint
1288html_object_get_line_length (HTMLObject *self, HTMLPainter *p, gint line_offset)
1289{
1290        return (* HO_CLASS (self)->get_line_length) (self, p, line_offset);
1291}
1292
1293guint
1294html_object_get_recursive_length (HTMLObject *self)
1295{
1296        return (* HO_CLASS (self)->get_recursive_length) (self);
1297}
1298
1299HTMLObject *
1300html_object_next_by_type (HTMLObject *self, HTMLType t)
1301{
1302        HTMLObject *next;
1303
1304        g_assert (self);
1305
1306        next = self->next;
1307        while (next && HTML_OBJECT_TYPE (next) != t)
1308                next = next->next;
1309
1310        return next;
1311}
1312
1313HTMLObject *
1314html_object_prev_by_type (HTMLObject *self, HTMLType t)
1315{
1316        HTMLObject *prev;
1317
1318        g_assert (self);
1319
1320        prev = self->prev;
1321        while (prev && HTML_OBJECT_TYPE (prev) != t)
1322                prev = prev->prev;
1323
1324        return prev;
1325}
1326
1327/* Movement functions */
1328
1329HTMLObject *
1330html_object_next (HTMLObject *self, HTMLObject *child)
1331{
1332        return (* HO_CLASS (self)->next) (self, child);
1333}
1334
1335HTMLObject *
1336html_object_prev (HTMLObject *self, HTMLObject *child)
1337{
1338        return (* HO_CLASS (self)->prev) (self, child);
1339}
1340
1341HTMLObject *
1342html_object_head (HTMLObject *self)
1343{
1344        return (* HO_CLASS (self)->head) (self);
1345}
1346
1347HTMLObject *
1348html_object_tail (HTMLObject *self)
1349{
1350        return (* HO_CLASS (self)->tail) (self);
1351}
1352
1353HTMLObject *
1354html_object_tail_not_slave (HTMLObject *self)
1355{
1356        HTMLObject *o = html_object_tail (self);
1357
1358        if (o && HTML_OBJECT_TYPE (o) == HTML_TYPE_TEXTSLAVE)
1359                o = html_object_prev_not_slave (o);
1360        return o;
1361}
1362
1363gboolean
1364html_object_cursor_forward (HTMLObject *self, HTMLCursor *cursor)
1365{
1366        gint len;
1367
1368        g_assert (self);
1369        g_assert (cursor->object == self);
1370
1371        if (html_object_is_container (self))
1372                return FALSE;
1373
1374        len = html_object_get_length (self);
1375        if (cursor->offset < len) {
1376                cursor->offset ++;
1377                cursor->position ++;
1378                return TRUE;
1379        } else
1380                return FALSE;
1381}
1382
1383gboolean
1384html_object_cursor_backward (HTMLObject *self, HTMLCursor *cursor)
1385{
1386        HTMLObject *prev;
1387
1388        g_assert (self);
1389        g_assert (cursor->object == self);
1390
1391        if (html_object_is_container (self))
1392                return FALSE;
1393
1394        if (cursor->offset > 1 || (cursor->offset > 0 && (! (prev = html_object_prev_not_slave (self))
1395                                                          || HTML_IS_CLUEALIGNED (prev)))) {
1396                cursor->offset --;
1397                cursor->position --;
1398                return TRUE;
1399        }
1400
1401        return FALSE;
1402}
1403
1404/*********************
1405 * movement on leafs
1406 */
1407
1408/* go up in tree so long as we can get object in neighborhood given by function next_fn */
1409
1410static HTMLObject *
1411next_object_uptree (HTMLObject *obj, HTMLObject * (*next_fn ) (HTMLObject *, HTMLObject *))
1412{
1413        HTMLObject *next = NULL;
1414
1415        while (obj->parent && !(next = (*next_fn) (obj->parent, obj)))
1416                obj = obj->parent;
1417
1418        return next;
1419}
1420
1421/* go down in tree to leaf in way given by down_fn children */
1422
1423static HTMLObject *
1424move_object_downtree (HTMLObject *obj, HTMLObject * (*down_fn ) (HTMLObject *))
1425{
1426        HTMLObject *down;
1427
1428        while ((down = (*down_fn) (obj)))
1429                obj = down;
1430
1431        return obj;
1432}
1433
1434static HTMLObject *
1435move_object (HTMLObject *obj, HTMLObject * (*next_fn ) (HTMLObject *, HTMLObject *), HTMLObject * (*down_fn ) (HTMLObject *))
1436{
1437        obj = next_object_uptree (obj, next_fn);
1438        if (obj)
1439                obj = move_object_downtree (obj, down_fn);
1440        return obj;
1441}
1442
1443inline HTMLObject *
1444html_object_next_leaf (HTMLObject *self)
1445{
1446        return move_object (self, html_object_next, html_object_head);
1447}
1448
1449HTMLObject *
1450html_object_next_leaf_not_type (HTMLObject *self, HTMLType t)
1451{
1452        HTMLObject *rv = self;
1453        while ((rv = html_object_next_leaf (rv)) && HTML_OBJECT_TYPE (rv) == t);
1454
1455        return rv;
1456}
1457
1458inline HTMLObject *
1459html_object_prev_leaf (HTMLObject *self)
1460{
1461        return move_object (self, html_object_prev, html_object_tail);
1462}
1463
1464HTMLObject *
1465html_object_prev_leaf_not_type (HTMLObject *self, HTMLType t)
1466{
1467        HTMLObject *rv = self;
1468        while ((rv = html_object_prev_leaf (rv)) && HTML_OBJECT_TYPE (rv) == t);
1469
1470        return rv;
1471}
1472
1473/* movement on cursor accepting objects */
1474
1475/* go up in tree so long as we can get object in neighborhood given by function next_fn */
1476
1477static HTMLObject *
1478next_object_uptree_cursor (HTMLObject *obj, HTMLObject * (*next_fn ) (HTMLObject *))
1479{
1480        HTMLObject *next = NULL;
1481
1482        while (obj->parent && !(next = (*next_fn) (obj))) {
1483                obj = obj->parent;
1484                if (html_object_accepts_cursor (obj))
1485                        return obj;
1486        }
1487
1488        return next;
1489}
1490
1491/* go down in tree to leaf in way given by down_fn children */
1492
1493static HTMLObject *
1494move_object_downtree_cursor (HTMLObject *obj, HTMLObject * (*down_fn ) (HTMLObject *))
1495{
1496        while ((obj = (*down_fn) (obj))) {
1497                if (html_object_accepts_cursor (obj))
1498                        break;
1499        }
1500
1501        return obj;
1502}
1503
1504static HTMLObject *
1505move_object_cursor (HTMLObject *obj, gint *offset, gboolean forward,
1506                    HTMLObject * (*next_fn ) (HTMLObject *), HTMLObject * (*down_fn ) (HTMLObject *))
1507{
1508        HTMLObject *down, *before;
1509
1510        do {
1511                gboolean found = FALSE;
1512                if (((*offset == 0 && forward) || (*offset && !forward)) && html_object_is_container (obj))
1513                        if ((down = (*down_fn) (obj))) {
1514                                down = move_object_downtree_cursor (down, down_fn);
1515                                if (down) {
1516                                        if (html_object_is_container (down))
1517                                                *offset = forward ? 0 : 1;
1518                                        return down;
1519                                }
1520                        }
1521
1522                before = obj;
1523                do {
1524                        obj = next_object_uptree_cursor (obj, next_fn);
1525                        if (obj) {
1526                                if (html_object_accepts_cursor (obj)) {
1527                                        if (html_object_is_container (obj))
1528                                                *offset = before->parent == obj->parent
1529                                                        ? forward ? 0 : 1
1530                                                        : forward ? 1 : 0;
1531                                        found = TRUE;
1532                                } else {
1533                                        HTMLObject *down;
1534                                        down = move_object_downtree_cursor (obj, down_fn);
1535                                        if (down) {
1536                                                if (html_object_is_container (down))
1537                                                        *offset = forward ? 0 : 1;
1538                                                obj = down;
1539                                                found = TRUE;
1540                                        }
1541                                }
1542                        }
1543                } while (obj && !found);
1544        } while (obj && !html_object_accepts_cursor (obj));
1545
1546        return obj;
1547}
1548
1549inline HTMLObject *
1550html_object_next_cursor (HTMLObject *self, gint *offset)
1551{
1552        return move_object_cursor (self, offset, TRUE, html_object_next_not_slave, html_object_head);
1553}
1554
1555inline HTMLObject *
1556html_object_prev_cursor (HTMLObject *self, gint *offset)
1557{
1558        return move_object_cursor (self, offset, FALSE, html_object_prev_not_slave, html_object_tail_not_slave);
1559}
1560
1561/***/
1562
1563guint
1564html_object_get_bytes (HTMLObject *self)
1565{
1566        return html_object_is_text (self) ? html_text_get_bytes (HTML_TEXT (self)) : html_object_get_length (self);
1567}
1568
1569guint
1570html_object_get_index (HTMLObject *self, guint offset)
1571{
1572        return html_object_is_text (self) ? html_text_get_index (HTML_TEXT (self), offset) : offset;
1573}
1574
1575void
1576html_object_set_data (HTMLObject *object, const gchar *key, const gchar *value)
1577{
1578        g_datalist_set_data_full (&object->object_data, key, g_strdup (value), g_free);
1579}
1580
1581gpointer
1582html_object_get_data (HTMLObject *object, const gchar *key)
1583{
1584        return g_datalist_get_data (&object->object_data, key);
1585}
1586
1587static void
1588copy_data (GQuark key_id, gpointer data, gpointer user_data)
1589{
1590        HTMLObject *o = HTML_OBJECT (user_data);
1591
1592        /* printf ("copy key: '%s'\n", g_quark_to_string (key_id)); */
1593        g_datalist_set_data_full (&o->object_data,
1594                                  g_strdup (g_quark_to_string (key_id)),
1595                                  g_strdup ((gchar *) data), g_free);
1596}
1597
1598void
1599html_object_copy_data_from_object (HTMLObject *dst, HTMLObject *src)
1600{
1601        g_datalist_foreach (&src->object_data, copy_data, dst);
1602}
1603
1604static void
1605object_save_data (GQuark key_id, gpointer data, gpointer user_data)
1606{
1607        HTMLEngineSaveState *state = (HTMLEngineSaveState *) user_data;
1608        const gchar *str;
1609
1610        str = html_engine_get_class_data (state->engine, state->save_data_class_name, g_quark_to_string (key_id));
1611        /* printf ("object %s %s\n", g_quark_to_string (key_id), str); */
1612        if (!str) {
1613                /* printf ("save %s %s -> %s\n", state->save_data_class_name, g_quark_to_string (key_id), (gchar *) data); */
1614                html_engine_save_output_string (state, "<!--+GtkHTML:<DATA class=\"%s\" key=\"%s\" value=\"%s\">",
1615                                                state->save_data_class_name, g_quark_to_string (key_id), data);
1616                html_engine_set_class_data (state->engine, state->save_data_class_name, g_quark_to_string (key_id), data);
1617        }
1618}
1619
1620static void
1621handle_object_data (gpointer key, gpointer value, gpointer data)
1622{
1623        HTMLEngineSaveState *state = (HTMLEngineSaveState *) data;
1624        gchar *str;
1625
1626        str = html_object_get_data (HTML_OBJECT (state->save_data_object), key);
1627        /* printf ("handle: %s %s %s %s\n", state->save_data_class_name, key, value, str); */
1628        if (!str) {
1629                /* printf ("clear\n"); */
1630                html_engine_save_output_string (state, "<!--+GtkHTML:<DATA class=\"%s\" clear=\"%s\">",
1631                                                state->save_data_class_name, key);
1632               
1633                state->data_to_remove = g_slist_prepend (state->data_to_remove, key);
1634        } else if (strcmp (value, str)) {
1635                /* printf ("change\n"); */
1636                html_engine_save_output_string (state, "<!--+GtkHTML:<DATA class=\"%s\" key=\"%s\" value=\"%s\">",
1637                                                state->save_data_class_name, key, str);
1638                html_engine_set_class_data (state->engine, state->save_data_class_name, key, value);
1639        }
1640}
1641
1642static void
1643clear_data (gchar *key, HTMLEngineSaveState *state)
1644{
1645        html_engine_clear_class_data (state->engine, state->save_data_class_name, key);
1646}
1647
1648gboolean
1649html_object_save_data (HTMLObject *self, HTMLEngineSaveState *state)
1650{
1651        if (state->engine->save_data) {
1652                GHashTable *t;
1653                state->save_data_class_name = html_type_name (self->klass->type);
1654                state->save_data_object = self;
1655                t = html_engine_get_class_table (state->engine, state->save_data_class_name);
1656                if (t) {
1657                        state->data_to_remove = NULL;
1658                        g_hash_table_foreach (t, handle_object_data, state);
1659                        g_slist_foreach (state->data_to_remove, (GFunc) clear_data, state);
1660                        g_slist_free (state->data_to_remove);
1661                        state->data_to_remove = NULL;
1662                }
1663                g_datalist_foreach (&self->object_data, object_save_data, state);
1664        }
1665
1666        return TRUE;
1667}
1668
1669GList *
1670html_object_get_bound_list (HTMLObject *self, GList *list)
1671{
1672        return list && list->next
1673                ? (HTML_OBJECT (list->data) == self ? list->next : NULL)
1674                : NULL;
1675}
1676
1677void
1678html_object_move_cursor_before_remove (HTMLObject *o, HTMLEngine *e)
1679{
1680        if (e->cursor->object == o) {
1681                if (html_object_next_not_slave (o))
1682                        e->cursor->object = html_object_next_not_slave (o);
1683                else
1684                        e->cursor->object = html_object_prev_not_slave (o);
1685        }
1686}
1687
1688gboolean
1689html_object_could_remove_whole (HTMLObject *o, GList *from, GList *to, GList *left, GList *right)
1690{
1691        return ((!from && !to)
1692                || html_object_next_not_slave (HTML_OBJECT (o))
1693                || html_object_prev_not_slave (HTML_OBJECT (o)))
1694                && ((!left  || o != left->data) && (!right || o != right->data));
1695
1696}
1697
1698void
1699html_object_check_cut_lists (HTMLObject *self, HTMLObject *replacement, GList *left, GList *right)
1700{
1701        if (left && left->data == self)
1702                left->data = replacement;
1703        if (right && right->data == self)
1704                right->data = replacement;
1705}
1706
1707
1708typedef struct {
1709        HTMLInterval *i;
1710        GString *buffer;
1711        gboolean in;
1712} tmpSelData;
1713
1714static void
1715select_object (HTMLObject *o, HTMLEngine *e, gpointer data)
1716{
1717        tmpSelData *d = (tmpSelData *) data;
1718
1719        if (o == d->i->from.object)
1720                d->in = TRUE;
1721        if (d->in)
1722                html_object_select_range (o, NULL,
1723                                          html_interval_get_start (d->i, o),
1724                                          html_interval_get_length (d->i, o), FALSE);
1725
1726        if (o == d->i->to.object)
1727                d->in = FALSE;
1728}
1729
1730static void
1731unselect_object (HTMLObject *o, HTMLEngine *e, gpointer data)
1732{
1733        o->selected = FALSE;
1734}
1735
1736gchar *
1737html_object_get_selection_string (HTMLObject *o, HTMLEngine *e)
1738{
1739        HTMLObject *tail;
1740        tmpSelData data;
1741        gchar *string;
1742
1743        g_assert (o);
1744
1745        tail        = html_object_get_tail_leaf (o);
1746        data.buffer = g_string_new (NULL);
1747        data.in     = FALSE;
1748        data.i      = html_interval_new (html_object_get_head_leaf (o), tail, 0, html_object_get_length (tail));
1749
1750        html_interval_forall (data.i, e, select_object, &data);
1751        html_object_append_selection_string (o, data.buffer);
1752        html_interval_forall (data.i, e, unselect_object, NULL);
1753
1754        html_interval_destroy (data.i);
1755        string = data.buffer->str;
1756        g_string_free (data.buffer, FALSE);
1757
1758        return string;
1759}
1760
1761HTMLObject *
1762html_object_get_tail_leaf (HTMLObject *o)
1763{
1764        HTMLObject *tail, *rv = o;
1765
1766        do {
1767                tail = html_object_tail_not_slave (rv);
1768                if (tail)
1769                        rv = tail;
1770        } while (tail);
1771
1772        return rv;
1773}
1774
1775HTMLObject *
1776html_object_get_head_leaf (HTMLObject *o)
1777{
1778        HTMLObject *head, *rv = o;
1779
1780        do {
1781                head = html_object_head (rv);
1782                if (head)
1783                        rv = head;
1784        } while (head);
1785
1786        return rv;
1787}
1788
1789static void
1790clear_word_width (HTMLObject *o, HTMLEngine *e, gpointer data)
1791{
1792        if (html_object_is_text (o))
1793                html_text_clear_word_width (HTML_TEXT (o));
1794}
1795
1796void
1797html_object_clear_word_width (HTMLObject *o)
1798{
1799        html_object_forall (o, NULL, clear_word_width, NULL);
1800}
1801
1802HTMLObject *
1803html_object_nth_parent (HTMLObject *self, gint n)
1804{
1805        while (self && n > 0) {
1806                self = self->parent;
1807                n --;
1808        }
1809
1810        return self;
1811}
1812
1813gint
1814html_object_get_parent_level (HTMLObject *self)
1815{
1816        gint level = 0;
1817
1818        while (self) {
1819                level ++;
1820                self = self->parent;
1821        }
1822
1823        return level;
1824}
1825
1826GList *
1827html_object_heads_list (HTMLObject *o)
1828{
1829        GList *list = NULL;
1830
1831        g_return_val_if_fail (o, NULL);
1832
1833        while (o) {
1834                list = g_list_append (list, o);
1835                o = html_object_head (o);
1836        }
1837
1838        return list;
1839}
1840
1841GList *
1842html_object_tails_list (HTMLObject *o)
1843{
1844        GList *list = NULL;
1845
1846        g_return_val_if_fail (o, NULL);
1847
1848        while (o) {
1849                list = g_list_append (list, o);
1850                o = html_object_tail_not_slave (o);
1851        }
1852
1853        return list;
1854}
1855
1856static void
1857merge_down (HTMLEngine *e, GList *left, GList *right)
1858{
1859        HTMLObject *lo;
1860        HTMLObject *ro;
1861
1862        while (left && right) {
1863                lo    = HTML_OBJECT (left->data);
1864                ro    = HTML_OBJECT (right->data);
1865                left  = left->next;
1866                right = right->next;
1867                if (!html_object_merge (lo, ro, e, &left, &right, NULL))
1868                        break;
1869        }
1870}
1871
1872void
1873html_object_merge_down (HTMLObject *o, HTMLObject *w, HTMLEngine *e)
1874{
1875        merge_down (e, html_object_tails_list (o), html_object_heads_list (w));
1876}
1877
1878gboolean
1879html_object_is_parent (HTMLObject *parent, HTMLObject *child)
1880{
1881        g_assert (parent && child);
1882
1883        while (child) {
1884                if (child->parent == parent)
1885                        return TRUE;
1886                child = child->parent;
1887        }
1888
1889        return FALSE;
1890}
1891
1892gint
1893html_object_get_insert_level (HTMLObject *o)
1894{
1895        switch (HTML_OBJECT_TYPE (o)) {
1896        case HTML_TYPE_TABLECELL:
1897        case HTML_TYPE_CLUEV:
1898                return 3;
1899        case HTML_TYPE_CLUEFLOW:
1900                return 2;
1901        default:
1902                return 1;
1903        }
1904}
1905
1906void
1907html_object_engine_translation (HTMLObject *o, HTMLEngine *e, gint *tx, gint *ty)
1908{
1909        HTMLObject *p;
1910
1911        *tx = 0;
1912        *ty = 0;
1913
1914        for (p = o->parent; p != NULL && HTML_OBJECT_TYPE (p) != HTML_TYPE_IFRAME; p = p->parent) {
1915                *tx += p->x;
1916                *ty += p->y - p->ascent;
1917        }
1918
1919        *tx = *tx + e->leftBorder - e->x_offset;
1920        *ty = *ty + e->topBorder - e->y_offset;
1921
1922}
1923
1924gboolean
1925html_object_engine_intersection (HTMLObject *o, HTMLEngine *e, gint tx, gint ty, gint *x1, gint *y1, gint *x2, gint *y2)
1926{
1927        *x1 = o->x + tx;
1928        *y1 = o->y - o->ascent + ty;
1929        *x2 = o->x + o->width + tx;
1930        *y2 = o->y + o->descent + ty;
1931
1932        return html_engine_intersection (e, x1, y1, x2, y2);
1933}
1934
1935void
1936html_object_add_to_changed (GList **changed_objs, HTMLObject *o)
1937{
1938        GList *l, *next;
1939
1940        if (!changed_objs || (*changed_objs && (*changed_objs)->data == o))
1941                return;
1942
1943        for (l = *changed_objs; l; l = next) {
1944                if (l->data == NULL) {
1945                        l = l->next;
1946                        next = l->next;
1947                        continue;
1948                }
1949                next = l->next;
1950                if (html_object_is_parent (o, HTML_OBJECT (l->data))) {
1951                        *changed_objs = g_list_remove_link (*changed_objs, l);
1952                        g_list_free (l);
1953                } else
1954                        break;
1955        }
1956
1957        *changed_objs = g_list_prepend (*changed_objs, o);
1958}
1959
1960HTMLClearType
1961html_object_get_clear (HTMLObject *self)
1962{
1963        return (* HO_CLASS (self)->get_clear) (self);
1964}
1965
1966static HTMLObject *
1967next_prev_cursor_object (HTMLObject *o, HTMLEngine *e, gint *offset, gboolean forward)
1968{
1969        HTMLCursor cursor;
1970        gboolean result;
1971
1972        html_cursor_init (&cursor, o, HTML_IS_TABLE (o) ? *offset : (forward ? html_object_get_length (o) : 0));
1973
1974        result = forward ? html_cursor_forward (&cursor, e) : html_cursor_backward (&cursor, e);
1975        *offset = cursor.offset;
1976
1977        return result ? cursor.object : NULL;
1978}
1979
1980HTMLObject *
1981html_object_next_cursor_object (HTMLObject *o, HTMLEngine *e, gint *offset)
1982{
1983        return next_prev_cursor_object (o, e, offset, TRUE);
1984}
1985
1986HTMLObject *
1987html_object_prev_cursor_object (HTMLObject *o, HTMLEngine *e, gint *offset)
1988{
1989        return next_prev_cursor_object (o, e, offset, FALSE);
1990}
1991
1992HTMLObject *
1993html_object_next_cursor_leaf (HTMLObject *o, HTMLEngine *e)
1994{
1995        gint offset = html_object_get_length (o);
1996
1997        o = html_object_next_cursor_object (o, e, &offset);
1998        while (o && html_object_is_container (o))
1999                o = html_object_next_cursor_object (o, e, &offset);
2000
2001        return o;
2002}
2003
2004HTMLObject *
2005html_object_prev_cursor_leaf (HTMLObject *o, HTMLEngine *e)
2006{
2007        gint offset = html_object_get_length (o);
2008
2009        o = html_object_prev_cursor_object (o, e, &offset);
2010        while (o && html_object_is_container (o))
2011                o = html_object_prev_cursor_object (o, e, &offset);
2012
2013        return o;
2014}
Note: See TracBrowser for help on using the repository browser.