source: trunk/third/gtkhtml3/src/htmlobject.c @ 21460

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