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

Revision 21460, 160.1 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 Anders Carlsson (andersca@gnu.org)
7    Copyright (C) 1999, 2000, Helix Code, Inc.
8    Copyright (C) 2001, 2002, 2003 Ximian Inc.
9
10    This library is free software; you can redistribute it and/or
11    modify it under the terms of the GNU Library General Public
12    License as published by the Free Software Foundation; either
13    version 2 of the License, or (at your option) any later version.
14
15    This library is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18    Library General Public License for more details.
19
20    You should have received a copy of the GNU Library General Public License
21    along with this library; see the file COPYING.LIB.  If not, write to
22    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23    Boston, MA 02111-1307, USA.
24*/
25
26/* RULE: You should never create a new flow without inserting anything in it.
27   If `e->flow' is not NULL, it must contain something.  */
28
29
30#include <config.h>
31#include "gtkhtml-compat.h"
32
33#include <stdio.h>
34#include <string.h>
35#include <stdlib.h>
36#include <ctype.h>
37#include <time.h>
38
39#include <gtk/gtkmain.h>
40#include <gtk/gtksignal.h>
41#include <gtk/gtkscrolledwindow.h>
42
43#include "gtkhtml-embedded.h"
44#include "gtkhtml-private.h"
45#include "gtkhtml-properties.h"
46#include "gtkhtml-stream.h"
47
48#include "gtkhtmldebug.h"
49
50#include "htmlengine.h"
51#include "htmlengine-search.h"
52#include "htmlengine-edit.h"
53#include "htmlengine-edit-cursor.h"
54#include "htmlengine-edit-movement.h"
55#include "htmlengine-edit-cut-and-paste.h"
56#include "htmlengine-edit-selection-updater.h"
57#include "htmlengine-print.h"
58#include "htmlcolor.h"
59#include "htmlinterval.h"
60#include "htmlobject.h"
61#include "htmlsettings.h"
62#include "htmltext.h"
63#include "htmltokenizer.h"
64#include "htmltype.h"
65#include "htmlundo.h"
66#include "htmldrawqueue.h"
67#include "htmlgdkpainter.h"
68#include "htmlplainpainter.h"
69#include "htmlreplace.h"
70#include "htmlentity.h"
71
72#include "htmlanchor.h"
73#include "htmlrule.h"
74#include "htmlobject.h"
75#include "htmlclueh.h"
76#include "htmlcluev.h"
77#include "htmlcluealigned.h"
78#include "htmlimage.h"
79#include "htmllist.h"
80#include "htmltable.h"
81#include "htmltablecell.h"
82#include "htmltext.h"
83#include "htmltextslave.h"
84#include "htmlclueflow.h"
85#include "htmlstack.h"
86#include "htmlstringtokenizer.h"
87#include "htmlselection.h"
88#include "htmlform.h"
89#include "htmlbutton.h"
90#include "htmltextinput.h"
91#include "htmlradio.h"
92#include "htmlcheckbox.h"
93#include "htmlhidden.h"
94#include "htmlselect.h"
95#include "htmltextarea.h"
96#include "htmlimageinput.h"
97#include "htmlstack.h"
98#include "htmlsearch.h"
99#include "htmlframeset.h"
100#include "htmlframe.h"
101#include "htmliframe.h"
102#include "htmlshape.h"
103#include "htmlmap.h"
104#include "htmlmarshal.h"
105#include "htmlstyle.h"
106
107/* #define CHECK_CURSOR */
108#ifdef CHECK_CURSOR
109#include <libgnomeui/gnome-dialog-util.h>
110#endif
111
112static void      html_engine_class_init       (HTMLEngineClass     *klass);
113static void      html_engine_init             (HTMLEngine          *engine);
114static gboolean  html_engine_timer_event      (HTMLEngine          *e);
115static gboolean  html_engine_update_event     (HTMLEngine          *e);
116static void      html_engine_queue_redraw_all (HTMLEngine *e);
117static char **   html_engine_stream_types     (GtkHTMLStream       *stream,
118                                               gpointer            data);
119static void      html_engine_stream_write     (GtkHTMLStream       *stream,
120                                               const gchar         *buffer,
121                                               size_t               size,
122                                               gpointer             data);
123static void      html_engine_stream_end       (GtkHTMLStream       *stream,
124                                               GtkHTMLStreamStatus  status,
125                                               gpointer             data);
126static void      html_engine_set_object_data  (HTMLEngine          *e,
127                                               HTMLObject          *o);
128
129static void      parse_one_token           (HTMLEngine *p,
130                                            HTMLObject *clue,
131                                            const gchar *str);
132static void      element_parse_input       (HTMLEngine *e,
133                                            HTMLObject *clue,
134                                            const gchar *s);
135static void      element_parse_iframe      (HTMLEngine *e,
136                                            HTMLObject *clue,
137                                            const gchar *s);
138static void      update_embedded           (GtkWidget *widget,
139                                            gpointer );
140
141static void      html_engine_map_table_clear (HTMLEngine *e);
142static void      html_engine_id_table_clear (HTMLEngine *e);
143static void      html_engine_add_map (HTMLEngine *e, const char *);
144static void      clear_pending_expose (HTMLEngine *e);
145
146static GtkLayoutClass *parent_class = NULL;
147
148enum {
149        SET_BASE_TARGET,
150        SET_BASE,
151        LOAD_DONE,
152        TITLE_CHANGED,
153        URL_REQUESTED,
154        DRAW_PENDING,
155        REDIRECT,
156        SUBMIT,
157        OBJECT_REQUESTED,
158        LAST_SIGNAL
159};
160       
161static guint signals [LAST_SIGNAL] = { 0 };
162
163#define TIMER_INTERVAL 300
164#define DT(x) ;
165#define DF(x) ;
166#define DE(x) ;
167
168#define ID_A "a"
169#define ID_ADDRESS "address"
170#define ID_B "b"
171#define ID_BIG "big"
172#define ID_BLOCKQUOTE "blockquote"
173#define ID_BODY "body"
174#define ID_CAPTION "caption"
175#define ID_CENTER "center"
176#define ID_CITE "cite"
177#define ID_CODE "code"
178#define ID_DIR "dir"
179#define ID_DIV "div"
180#define ID_DL "dl"
181#define ID_DT "dt"
182#define ID_DD "dd"
183#define ID_LI "li"
184#define ID_EM "em"
185#define ID_FONT "font"
186#define ID_FORM "form"
187#define ID_MAP "map"
188#define ID_HEADING "h"
189#define ID_I "i"
190#define ID_KBD "kbd"
191#define ID_OL "ol"
192#define ID_P "p"
193#define ID_PRE "pre"
194#define ID_SMALL "small"
195#define ID_SPAN "span"
196#define ID_STRONG "strong"
197#define ID_U "u"
198#define ID_UL "ul"
199#define ID_TEXTAREA "textarea"
200#define ID_TABLE "table"
201#define ID_TD "td"
202#define ID_TH "th"
203#define ID_TR "tr"
204#define ID_TT "tt"
205#define ID_VAR "var"
206#define ID_S "s"
207#define ID_SUB "sub"
208#define ID_SUP "sup"
209#define ID_STRIKE "strike"
210#define ID_HTML "html"
211#define ID_DOCUMENT "Document"
212#define ID_OPTION "option"
213#define ID_SELECT "select"
214#define ID_TEST "test"
215
216#define ID_EQ(x,y) (x == g_quark_from_string (y))
217
218
219
220/*
221 *  Font handling.
222 */
223
224/* Font styles */
225typedef struct _HTMLElement HTMLElement;
226typedef void (*BlockFunc)(HTMLEngine *e, HTMLObject *clue, HTMLElement *el);
227struct _HTMLElement {
228        GQuark          id;
229        HTMLStyle      *style;
230
231        GHashTable     *attributes;  /* the parsed attributes */
232
233        gint level;
234        gint miscData1;
235        gint miscData2;
236        BlockFunc exitFunc;
237};
238
239static char *
240parse_element_name (const char *str)
241{
242        const char *ep = str;
243
244        ep = str;
245        if (*ep == '/')
246                ep++;
247
248        while (*ep && *ep != ' ' && *ep != '>' && *ep != '/')
249                ep++;
250       
251        if (ep - str == 0 || (*str == '/' && ep - str == 1)) {
252                g_warning ("found token with no valid name");
253                return NULL;
254        }
255
256        return g_strndup (str, ep - str);
257}
258
259
260static HTMLElement *
261html_element_new (HTMLEngine *e, const char *str) {
262        HTMLElement *element;
263        char *name;
264
265        name = parse_element_name (str);
266       
267        if (!name)
268                return NULL;
269
270        element = g_new0 (HTMLElement, 1);
271        element->id = g_quark_from_string (name);
272
273        element->attributes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
274
275        html_string_tokenizer_tokenize (e->st, str + strlen (name), " >");
276        g_free (name);
277
278        while (html_string_tokenizer_has_more_tokens (e->st)) {
279                const gchar *token = html_string_tokenizer_next_token (e->st);
280                gchar **attr;
281
282                DE(g_print ("token = %s\n", token));
283                attr = g_strsplit (token, "=", 2);
284               
285                if (attr[0]) {
286                        char *lower = g_ascii_strdown (attr[0], -1);
287
288                        if (!g_hash_table_lookup (element->attributes, lower)) {
289                                DE (g_print ("attrs (%s, %s)", attr[0], attr[1]));
290                                g_hash_table_insert (element->attributes, lower, g_strdup (attr[1]));
291                        } else
292                                g_free (lower);
293                }
294
295                g_strfreev (attr);
296        }
297       
298        return element;
299}
300
301#ifndef NO_ATTR_MACRO
302/* Macro definition to avoid bogus warnings about strict aliasing.
303 */
304#  if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3)
305#    define html_element_get_attr(node, key, value) ({                                  \
306        gpointer _tmp_;                                                                 \
307        (g_hash_table_lookup_extended (node->attributes, key, NULL, &_tmp_) && _tmp_) ? \
308           (*value = _tmp_, TRUE) : FALSE;                                              \
309    })
310#  else
311#    define html_element_get_attr(node, key, value) (g_hash_table_lookup_extended (node->attributes, key, NULL, (gpointer *)value) && *value)
312#  endif
313#define html_element_has_attr(node, key) g_hash_table_lookup_extended (node->attributes, key, NULL, NULL)
314#else
315gboolean
316html_element_get_attr (HTMLElement *node, char *name, char **value)
317{
318        char *orig_key;
319
320        g_return_if_fail (node->attributes != NULL);
321
322        return g_hash_table_lookup_extended (node->attributes, name, &orig_key, value)
323}
324#endif
325
326#if 0
327static void
328html_element_parse_i18n (HTMLElement *node)
329{
330        char *value;
331        /*
332          <!ENTITY % i18n
333          "lang        %LanguageCode; #IMPLIED  -- language code --
334          dir         (ltr|rtl)      #IMPLIED  -- direction for weak/neutral text --"
335          >
336        */
337       
338        if (html_element_get_attr (node, "dir", &value)) {
339                printf ("dir = %s\n", value);
340        }
341
342        if (html_element_get_attr (node, "lang", &value)) {
343                printf ("lang = %s\n", value);
344        }
345}
346#endif
347
348static void
349html_element_parse_coreattrs (HTMLElement *node)
350{
351        char *value;
352       
353        /*
354          <!ENTITY % coreattrs
355          "id          ID             #IMPLIED  -- document-wide unique id --
356          class       CDATA          #IMPLIED  -- space-separated list of classes --
357          style       %StyleSheet;   #IMPLIED  -- associated style info --
358          title       %Text;         #IMPLIED  -- advisory title --"
359          >
360        */
361        if (html_element_get_attr (node, "style", &value)) {
362                node->style = html_style_add_attribute (node->style, value);
363        }
364}
365
366#if 0
367static void
368html_element_parse_events (HTMLElement *node)
369{
370        /*
371           <!ENTITY % events
372           "onclick     %Script;       #IMPLIED  -- a pointer button was clicked --
373           ondblclick  %Script;       #IMPLIED  -- a pointer button was double clicked--
374           onmousedown %Script;       #IMPLIED  -- a pointer button was pressed down --
375           onmouseup   %Script;       #IMPLIED  -- a pointer button was released --
376           onmouseover %Script;       #IMPLIED  -- a pointer was moved onto --
377           onmousemove %Script;       #IMPLIED  -- a pointer was moved within --
378           onmouseout  %Script;       #IMPLIED  -- a pointer was moved away --
379           onkeypress  %Script;       #IMPLIED  -- a key was pressed and released --
380           onkeydown   %Script;       #IMPLIED  -- a key was pressed down --
381           onkeyup     %Script;       #IMPLIED  -- a key was released --"
382           >
383        */
384}
385#endif
386
387static void
388html_element_free (HTMLElement *element)
389{
390        g_hash_table_destroy (element->attributes);
391
392        html_style_free (element->style);
393        g_free (element);
394}
395
396static void
397push_element (HTMLEngine *e, char *name, char *class, HTMLStyle *style)
398{
399        HTMLElement *element = g_new0 (HTMLElement, 1);
400
401        element->id = g_quark_from_string (name);
402        element->style = html_style_set_display (style, DISPLAY_INLINE);
403        html_stack_push (e->span_stack, element);
404}
405
406static void
407free_element (gpointer data)
408{
409        HTMLElement *span = data;
410
411        html_style_free (span->style);
412        g_free (span);
413}
414
415#define DI(x)
416
417static HTMLColor *
418current_color (HTMLEngine *e) {
419        HTMLElement *span;
420        GList *item;
421
422        for (item = e->span_stack->list; item; item = item->next) {
423                span = item->data;
424               
425                if (span->style->display >= DISPLAY_TABLE_CELL)
426                        break;
427
428                if (span->style && span->style->color)
429                        return span->style->color;
430        }
431
432        return html_colorset_get_color (e->settings->color_set, HTMLTextColor);
433}
434
435static GdkColor *
436current_bg_color (HTMLEngine *e) {
437        HTMLElement *span;
438        GList *item;
439
440        for (item = e->span_stack->list; item; item = item->next) {
441                span = item->data;
442               
443                if (span->style->display >= DISPLAY_TABLE_CELL)
444                        break;
445
446                if (span->style && span->style->bg_color)
447                        return &span->style->bg_color->color;
448        }
449
450        return NULL;
451}
452
453/*
454 * FIXME these are 100% wrong (bg color doesn't inheirit, but it is how the current table code works
455 * and I don't want to regress yet
456 */
457static HTMLColor *
458current_row_bg_color (HTMLEngine *e)
459{
460        HTMLElement *span;
461        GList *item;
462               
463        for (item = e->span_stack->list; item; item = item->next) {
464                span = item->data;
465                if (span->style->display == DISPLAY_TABLE_ROW)
466                        return span->style->bg_color;
467
468                if (span->style->display == DISPLAY_TABLE)
469                        break;
470        }
471
472        return NULL;
473}
474
475static char *
476current_row_bg_image (HTMLEngine *e)
477{
478        HTMLElement *span;
479        GList *item;
480               
481        for (item = e->span_stack->list; item; item = item->next) {
482                span = item->data;
483                if (span->style->display == DISPLAY_TABLE_ROW)
484                        return span->style->bg_image;
485
486                if (span->style->display == DISPLAY_TABLE)
487                        break;
488        }
489
490        return NULL;
491}
492
493static HTMLVAlignType
494current_row_valign (HTMLEngine *e)
495{
496        HTMLElement *span;
497        GList *item;
498        HTMLVAlignType rv = HTML_VALIGN_MIDDLE;
499
500        if (!html_stack_top (e->table_stack)) {
501                DT (g_warning ("missing table");)
502                return rv;
503        }
504
505        for (item = e->span_stack->list; item; item = item->next) {
506                span = item->data;
507                if (span->style->display == DISPLAY_TABLE_ROW) {
508                        DT(g_warning ("found row");)
509
510                        rv = span->style->text_valign;
511
512                        break;
513                }
514
515                if (span->style->display == DISPLAY_TABLE) {
516                        DT(g_warning ("found table before row");)
517                        break;
518                }
519        }
520
521        DT(
522        if (ID_EQ (span->id, ID_TR))
523                DT(g_warning ("no row");)
524        );
525
526        return rv;
527}
528
529static HTMLHAlignType
530current_row_align (HTMLEngine *e)
531{
532        HTMLElement *span;
533        GList *item;
534        HTMLHAlignType rv = HTML_HALIGN_NONE;
535
536        if (!html_stack_top (e->table_stack)) {
537                DT (g_warning ("missing table");)
538                return rv;
539        }
540
541        for (item = e->span_stack->list; item; item = item->next) {
542                span = item->data;
543                if (span->style->display == DISPLAY_TABLE_ROW) {
544                        DT(g_warning ("found row");)
545
546                        if (span->style)
547                                rv = span->style->text_align;
548
549                        break;
550                }
551
552                if (span->style->display == DISPLAY_TABLE) {
553                        DT(g_warning ("found table before row");)
554                        break;
555                }
556        }
557
558        DT(
559        if (ID_EQ (span->id, ID_TR))
560                DT(g_warning ("no row");)
561        );
562        return rv;
563}
564/* end of these table hacks */
565       
566static HTMLFontFace *
567current_font_face (HTMLEngine *e)
568{
569
570
571        HTMLElement *span;
572        GList *item;
573       
574        for (item = e->span_stack->list; item; item = item->next) {
575                span = item->data;
576                if (span->style && span->style->face)
577                        return span->style->face;
578
579        }
580
581        return NULL;
582}
583
584static inline GtkHTMLFontStyle
585current_font_style (HTMLEngine *e)
586{
587        HTMLElement *span;
588        GList *item;
589        GtkHTMLFontStyle style = GTK_HTML_FONT_STYLE_DEFAULT;
590       
591        for (item = e->span_stack->list; item && item->next; item = item->next) {
592                span = item->data;
593                if (span->style->display == DISPLAY_TABLE_CELL)
594                        break;
595        }
596
597        for (; item; item = item->prev) {
598                span = item->data;
599                style = (style & ~span->style->mask) | (span->style->settings & span->style->mask);
600        }
601        return style;
602}
603
604static HTMLHAlignType
605current_alignment (HTMLEngine *e)
606{
607        HTMLElement *span;
608        GList *item;
609        gint maxLevel = 0;
610
611        for (item = e->span_stack->list; item; item = item->next) {
612                span = item->data;
613
614                /* we track the max display level here because an alignment on
615                 * an inline block should not change change the block alignment
616                 * unless the block is nested in the inline element
617                 */
618                maxLevel = MAX (maxLevel, span->style->display);
619               
620                if (span->style->display >= DISPLAY_TABLE_CELL)
621                        break;
622
623                if (span->style->text_align != HTML_HALIGN_NONE && maxLevel >= DISPLAY_BLOCK)
624                        return span->style->text_align;
625               
626        }
627        return HTML_HALIGN_NONE;
628}
629       
630static GtkPolicyType
631parse_scroll (const char *token)
632{
633        GtkPolicyType scroll;
634       
635        if (strncasecmp (token, "yes", 3) == 0) {
636                scroll = GTK_POLICY_ALWAYS;
637        } else if (strncasecmp (token, "no", 2) == 0) {
638                scroll = GTK_POLICY_NEVER;
639        } else /* auto */ {
640                scroll = GTK_POLICY_AUTOMATIC;
641        }
642        return scroll;
643}
644
645static HTMLHAlignType
646parse_halign (const char *token, HTMLHAlignType default_val)
647{
648        if (strcasecmp (token, "right") == 0)
649                return HTML_HALIGN_RIGHT;
650        else if (strcasecmp (token, "left") == 0)
651                return HTML_HALIGN_LEFT;
652        else if (strcasecmp (token, "center") == 0 || strcasecmp (token, "middle") == 0)
653                return HTML_HALIGN_CENTER;
654        else
655                return default_val;
656}
657       
658
659/* ClueFlow style handling.  */
660
661static HTMLClueFlowStyle
662current_clueflow_style (HTMLEngine *e)
663{
664        HTMLClueFlowStyle style;
665
666        if (html_stack_is_empty (e->clueflow_style_stack))
667                return HTML_CLUEFLOW_STYLE_NORMAL;
668
669        style = (HTMLClueFlowStyle) GPOINTER_TO_INT (html_stack_top (e->clueflow_style_stack));
670        return style;
671}
672
673static void
674push_clueflow_style (HTMLEngine *e,
675                     HTMLClueFlowStyle style)
676{
677        html_stack_push (e->clueflow_style_stack, GINT_TO_POINTER (style));
678}
679
680static void
681pop_clueflow_style (HTMLEngine *e)
682{
683        html_stack_pop (e->clueflow_style_stack);
684}
685
686
687
688/* Utility functions.  */
689
690static void new_flow (HTMLEngine *e, HTMLObject *clue, HTMLObject *first_object, HTMLClearType clear);
691static void close_flow (HTMLEngine *e, HTMLObject *clue);
692static void finish_flow (HTMLEngine *e, HTMLObject *clue);
693static void pop_element (HTMLEngine *e, char *name);
694
695static HTMLObject *
696text_new (HTMLEngine *e, const gchar *text, GtkHTMLFontStyle style, HTMLColor *color)
697{
698        HTMLObject *o;
699
700        o = html_text_new (text, style, color);
701        html_engine_set_object_data (e, o);
702
703        return o;
704}
705
706static HTMLObject *
707flow_new (HTMLEngine *e, HTMLClueFlowStyle style, HTMLListType item_type, gint item_number, HTMLClearType clear)
708{
709        HTMLObject *o;
710        GByteArray *levels;
711        GList *l;
712
713        levels = g_byte_array_new ();
714       
715        if (e->listStack && e->listStack->list) {
716                l = e->listStack->list;
717                while (l) {
718                        guint8 val = ((HTMLList *)l->data)->type;
719
720                        g_byte_array_prepend (levels, &val, 1);
721                        l = l->next;
722                }
723        }
724
725        o = html_clueflow_new (style, levels, item_type, item_number, clear);   
726        html_engine_set_object_data (e, o);
727
728        return o;
729}
730
731static HTMLObject *
732create_empty_text (HTMLEngine *e)
733{
734        HTMLObject *o;
735
736        o = text_new (e, "", current_font_style (e), current_color (e));
737        html_text_set_font_face (HTML_TEXT (o), current_font_face (e));
738
739        return o;
740}
741
742static void
743add_line_break (HTMLEngine *e,
744                HTMLObject *clue,
745                HTMLClearType clear)
746{
747        if (!e->flow)
748                new_flow (e, clue, create_empty_text (e), HTML_CLEAR_NONE);
749        new_flow (e, clue, NULL, clear);
750}
751
752static void
753finish_flow (HTMLEngine *e, HTMLObject *clue) {
754        if (e->flow && HTML_CLUE (e->flow)->tail == NULL) {
755                html_clue_remove (HTML_CLUE (clue), e->flow);
756                html_object_destroy (e->flow);
757                e->flow = NULL;
758        }
759        close_flow (e, clue);
760}
761
762
763static void
764close_flow (HTMLEngine *e, HTMLObject *clue)
765{
766        HTMLObject *last;
767
768        if (e->flow == NULL)
769                return;
770
771        last = HTML_CLUE (e->flow)->tail;
772        if (last == NULL) {
773                html_clue_append (HTML_CLUE (e->flow), create_empty_text (e));
774        } else if (last != HTML_CLUE (e->flow)->head
775                   && html_object_is_text (last)
776                   && HTML_TEXT (last)->text_len == 1
777                   && HTML_TEXT (last)->text [0] == ' ') {
778                html_clue_remove (HTML_CLUE (e->flow), last);
779                html_object_destroy (last);
780        }
781
782        e->flow = NULL;
783}
784
785static void
786update_flow_align (HTMLEngine *e, HTMLObject *clue)
787{
788        if (e->flow != NULL) {
789                if (HTML_CLUE (e->flow)->head != NULL)
790                        close_flow (e, clue);
791                else
792                        HTML_CLUE (e->flow)->halign = current_alignment (e);
793        }
794}
795
796static void
797new_flow (HTMLEngine *e, HTMLObject *clue, HTMLObject *first_object, HTMLClearType clear)
798{
799        close_flow (e, clue);
800
801        e->flow = flow_new (e, current_clueflow_style (e), HTML_LIST_TYPE_BLOCKQUOTE, 0, clear);
802
803        HTML_CLUE (e->flow)->halign = current_alignment (e);
804
805        if (first_object)
806                html_clue_append (HTML_CLUE (e->flow), first_object);
807
808        html_clue_append (HTML_CLUE (clue), e->flow);
809}
810
811static void
812append_element (HTMLEngine *e,
813                HTMLObject *clue,
814                HTMLObject *obj)
815{
816        e->avoid_para = FALSE;
817
818        if (e->flow == NULL)
819                new_flow (e, clue, obj, HTML_CLEAR_NONE);
820        else
821                html_clue_append (HTML_CLUE (e->flow), obj);
822}
823
824static void
825apply_attributes (HTMLText *text, HTMLEngine *e, GtkHTMLFontStyle style, HTMLColor *color, GdkColor *bg_color, gint last_pos, gboolean link)
826{
827        PangoAttribute *attr;
828
829        html_text_set_style_in_range (text, style, e, last_pos, text->text_bytes);
830
831        /* color */
832        if (color != html_colorset_get_color (e->settings->color_set, HTMLTextColor))
833                html_text_set_color_in_range (text, color, last_pos, text->text_bytes);
834
835        if (bg_color) {
836                attr = pango_attr_background_new (bg_color->red, bg_color->green, bg_color->blue);
837                attr->start_index = last_pos;
838                attr->end_index = text->text_bytes;
839                pango_attr_list_change (text->attr_list, attr);
840        }
841
842        /* face */
843}
844
845static void
846insert_text (HTMLEngine *e,
847             HTMLObject *clue,
848             const gchar *text)
849{
850        GtkHTMLFontStyle font_style;
851        HTMLObject *prev;
852        HTMLColor *color;
853        gchar *face;
854        gboolean create_link;
855        gint last_pos = 0;
856        gint last_bytes = 0;
857
858        if (text [0] == ' ' && text [1] == 0) {
859                if (e->eat_space)
860                        return;
861                else
862                        e->eat_space = TRUE;
863        } else
864                e->eat_space = FALSE;
865       
866
867        if (e->url != NULL || e->target != NULL)
868                create_link = TRUE;
869        else
870                create_link = FALSE;
871
872        font_style = current_font_style (e);
873        color = current_color (e);
874        face = current_font_face (e);
875
876        if ((create_link || e->flow == NULL || HTML_CLUE (e->flow)->head == NULL) && !e->inPre) {
877                while (*text == ' ')
878                        text++;
879                if (*text == 0)
880                        return;
881        }
882
883        if (e->flow == NULL)
884                prev = NULL;
885        else
886                prev = HTML_CLUE (e->flow)->tail;
887
888        if (!prev || !HTML_IS_TEXT (prev)) {
889                prev = text_new (e, text, font_style, color);
890                append_element (e, clue, prev);
891        } else {
892                last_pos = HTML_TEXT (prev)->text_len;
893                last_bytes = HTML_TEXT (prev)->text_bytes;
894                html_text_append (HTML_TEXT (prev), text, -1);
895        }
896
897        if (prev && HTML_IS_TEXT (prev)) {
898                apply_attributes (HTML_TEXT (prev), e, font_style, color, current_bg_color (e), last_bytes, create_link);
899                if (create_link)
900                        html_text_append_link (HTML_TEXT (prev), e->url, e->target, last_pos, HTML_TEXT (prev)->text_len);
901        }
902}
903
904
905static void block_end_div (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem);
906static void block_end_row (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem);
907static void block_end_cell (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem);
908static void pop_element_by_type (HTMLEngine *e, HTMLDisplayType display);
909
910/* Block stack.  */
911static void
912html_element_push (HTMLElement *node, HTMLEngine *e, HTMLObject *clue)
913{
914        switch (node->style->display) {
915        case DISPLAY_BLOCK:
916                /* close anon p elements */
917                pop_element (e, ID_P);
918                html_stack_push (e->span_stack, node);
919#if TESTING
920                if (node->style->bg_color) {
921                        HTMLTableCell *cell;
922                        cell = html_table_cell_new (1, 1, 0);
923                        html_table_cell_set_fixed_width (cell, 50, 0);
924
925                        html_object_set_bg_color (HTML_OBJECT (cell), node->style->bg_color);
926                        append_element (e, clue, HTML_OBJECT (cell));
927                        push_clue (e, HTML_OBJECT (cell));
928                        node->exitFunc = block_end_cell;
929                } else {
930                        node->exitFunc = block_end_div;
931                }
932#else
933                node->exitFunc = block_end_div;
934#endif
935                update_flow_align (e, clue);
936                break;
937        case DISPLAY_TABLE_ROW:
938                {
939                        HTMLTable *table = html_stack_top (e->table_stack);
940
941                        if (!table) {
942                                html_element_free (node);
943                                return;
944                        }
945               
946                        pop_element_by_type (e, DISPLAY_TABLE_CAPTION);
947                        pop_element_by_type (e, DISPLAY_TABLE_ROW);
948                       
949                        html_table_start_row (table);
950                       
951                        node->exitFunc = block_end_row;
952                        html_stack_push (e->span_stack, node);
953                }
954                break;
955        case DISPLAY_INLINE:
956        default:
957                html_stack_push (e->span_stack, node);
958                break;
959        }
960}
961
962static void
963push_block_element (HTMLEngine *e,
964                    char *name,
965                    HTMLStyle *style,
966                    HTMLDisplayType level,
967                    BlockFunc exitFunc,
968                    gint miscData1,
969                    gint miscData2)
970{
971        HTMLElement *element = g_new0 (HTMLElement, 1);
972       
973        element->id = g_quark_from_string (name);
974        element->style = html_style_set_display (style, level);
975        element->exitFunc = exitFunc;
976        element->miscData1 = miscData1;
977        element->miscData2 = miscData2;
978
979        if (element->style->display == DISPLAY_BLOCK)
980                pop_element (e, ID_P);
981       
982        html_stack_push (e->span_stack, element);
983}
984
985
986static void
987push_block (HTMLEngine *e,
988            char *name,
989            gint level,
990            BlockFunc exitFunc,
991            gint miscData1,
992            gint miscData2)
993{
994        push_block_element (e, name, NULL, level, exitFunc, miscData1, miscData2);
995}
996
997static GList *
998remove_element (HTMLEngine *e, GList *item)
999{
1000        HTMLElement *elem = item->data;
1001        GList *next = item->next;
1002
1003        /* CLUECHECK */
1004        if (elem->exitFunc)
1005                (*(elem->exitFunc))(e, e->parser_clue, elem);
1006
1007        e->span_stack->list = g_list_remove_link (e->span_stack->list, item);
1008
1009        g_list_free (item);
1010        free_element (elem);
1011       
1012        return next;
1013}
1014
1015static void
1016pop_block (HTMLEngine *e, HTMLElement *elem)
1017{
1018        GList *l = e->span_stack->list;
1019
1020        while (l) {
1021                HTMLElement *cur = l->data;
1022
1023                if (cur == elem) {
1024                        l = remove_element (e, l);
1025                        return;
1026                } else if (cur->style->display != DISPLAY_INLINE || elem->style->display > DISPLAY_BLOCK) {
1027                        l = remove_element (e, l);
1028                } else {
1029                        l = l->next;
1030                }
1031        }
1032}
1033
1034static void
1035pop_inline (HTMLEngine *e, HTMLElement *elem)
1036{
1037        GList *l = e->span_stack->list;
1038
1039        while (l) {
1040                HTMLElement *cur = l->data;
1041
1042                if (cur->level > DISPLAY_BLOCK)
1043                        break;
1044
1045                if (cur == elem) {
1046                        l = remove_element (e, l);
1047                        return;
1048                } else {
1049                        l = l->next;
1050                }
1051        }
1052}
1053
1054static void
1055pop_element_by_type (HTMLEngine *e, HTMLDisplayType display)
1056{
1057        HTMLElement *elem = NULL;
1058        GList *l;
1059        gint maxLevel = display;
1060
1061        l = e->span_stack->list;
1062       
1063        while (l) {
1064                gint cd;
1065                elem = l->data;
1066               
1067                cd = elem->style->display;
1068                if (cd == display)
1069                        break;
1070
1071                if (cd > maxLevel) {
1072                        if (display != DISPLAY_INLINE
1073                            || cd > DISPLAY_BLOCK)
1074                                return;
1075                }
1076
1077                l = l->next;
1078        }
1079                 
1080        if (l == NULL)
1081                return;
1082
1083        if (display == DISPLAY_INLINE) {
1084                pop_inline (e, elem);
1085        } else {
1086                if (maxLevel > display)
1087                        return;
1088       
1089                pop_block (e, elem);
1090        }
1091}
1092
1093
1094static void
1095pop_element (HTMLEngine *e, char *name)
1096{
1097        HTMLElement *elem = NULL;
1098        GList *l;
1099        gint maxLevel;
1100        GQuark id = g_quark_from_string (name);
1101
1102        l = e->span_stack->list;
1103        maxLevel = 0;
1104
1105        while (l) {
1106                elem = l->data;
1107
1108                if (elem->id == id)
1109                        break;
1110
1111                maxLevel = MAX (maxLevel, elem->style->display);
1112                l = l->next;
1113        }
1114
1115        if (l == NULL)
1116                return;
1117
1118        if (elem->style->display == DISPLAY_INLINE) {
1119                pop_inline (e, elem);
1120        } else {
1121                if (maxLevel > elem->style->display)
1122                        return;
1123       
1124                pop_block (e, elem);
1125        }
1126}
1127
1128/* The following are callbacks that are called at the end of a block.  */
1129static void
1130block_end_div (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1131{
1132        close_flow (e, clue);
1133}
1134
1135static void
1136block_end_p (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1137{
1138        if (e->avoid_para) {
1139                finish_flow (e, clue);
1140        } else {
1141                new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1142                new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1143                e->avoid_para = TRUE;
1144        }
1145}
1146
1147static void
1148block_end_map (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1149{
1150        e->map = NULL;
1151}
1152
1153static void
1154block_end_option (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1155{
1156        if ( e->inOption )
1157                html_select_set_text (e->formSelect, e->formText->str);
1158       
1159        e->inOption = FALSE;
1160}
1161
1162static void
1163block_end_select (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1164{
1165        if ( e->inOption )
1166                html_select_set_text (e->formSelect, e->formText->str);
1167       
1168        e->inOption = FALSE;
1169        e->formSelect = NULL;
1170        e->eat_space = FALSE;
1171}
1172
1173static void
1174block_end_textarea (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1175{
1176        if ( e->inTextArea )
1177                html_textarea_set_text (e->formTextArea, e->formText->str);
1178       
1179        e->inTextArea = FALSE;
1180        e->formTextArea = NULL;
1181        e->eat_space = FALSE;
1182}
1183
1184static void
1185push_clue_style (HTMLEngine *e)
1186{
1187        //html_stack_push (e->body_stack, e->span_stack);
1188        html_stack_push (e->body_stack, e->clueflow_style_stack);
1189        html_stack_push (e->body_stack, e->listStack);
1190        /* CLUECHECK */
1191
1192        //e->span_stack = html_stack_new (free_elementggs);
1193        e->clueflow_style_stack = html_stack_new (NULL);
1194        e->listStack = html_stack_new ((HTMLStackFreeFunc)html_list_destroy);
1195
1196        html_stack_push (e->body_stack, GINT_TO_POINTER (e->avoid_para));
1197        e->avoid_para = TRUE;
1198
1199        html_stack_push (e->body_stack, GINT_TO_POINTER (e->inPre));
1200        e->inPre = 0;
1201}
1202
1203static void
1204push_clue (HTMLEngine *e, HTMLObject *clue)
1205{
1206        push_clue_style (e);
1207
1208        html_stack_push (e->body_stack, e->parser_clue);
1209        html_stack_push (e->body_stack, e->flow);
1210        e->parser_clue = clue;
1211        e->flow = NULL;
1212}
1213
1214static void
1215pop_clue_style (HTMLEngine *e)
1216{
1217        /* CLUECHECK */
1218        finish_flow (e, HTML_OBJECT (e->parser_clue));
1219
1220        e->inPre = GPOINTER_TO_INT (html_stack_pop (e->body_stack));
1221        e->avoid_para = GPOINTER_TO_INT (html_stack_pop (e->body_stack));
1222       
1223        html_stack_destroy (e->clueflow_style_stack);
1224        //html_stack_destroy (e->span_stack);
1225
1226        /* CLUECHECK */
1227        e->listStack = html_stack_pop (e->body_stack);
1228        e->clueflow_style_stack = html_stack_pop (e->body_stack);
1229        //e->span_stack = html_stack_pop (e->body_stack);
1230}
1231
1232static void
1233pop_clue (HTMLEngine *e)
1234{
1235        e->flow = html_stack_pop (e->body_stack);
1236        e->parser_clue = html_stack_pop (e->body_stack);
1237
1238        pop_clue_style (e);
1239}
1240
1241static void
1242block_end_cell (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1243{
1244        if (html_clue_is_empty (HTML_CLUE (clue)))
1245                new_flow (e, clue, create_empty_text (e), HTML_CLEAR_NONE);
1246        pop_clue (e);
1247}
1248
1249
1250/* docment section parsers */
1251static void
1252block_end_title (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1253{
1254        /*
1255         * only emit the title changed signal if we have a
1256         * valid title
1257         */
1258        if (e->inTitle && e->title)
1259                g_signal_emit (e, signals [TITLE_CHANGED], 0);
1260        e->inTitle = FALSE;
1261}
1262
1263static void
1264element_parse_title (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1265{
1266        e->inTitle = TRUE;
1267        e->title = g_string_new ("");
1268       
1269        push_block (e, "title", DISPLAY_NONE, block_end_title, 0, 0);
1270}
1271
1272static void
1273parse_text (HTMLEngine *e, HTMLObject *clue, char *str)
1274{
1275        if (e->inOption || e->inTextArea) {
1276                g_string_append (e->formText, str);
1277        } else if (e->inTitle) {
1278                g_string_append (e->title, str);
1279        } else {
1280                insert_text (e, clue, str);
1281        }
1282}
1283
1284static gchar *
1285new_parse_body (HTMLEngine *e, const gchar *end[])
1286{
1287        HTMLObject *clue = NULL;
1288        gchar *str;
1289        gchar *rv = NULL;
1290
1291        e->eat_space = FALSE;
1292
1293        while (html_tokenizer_has_more_tokens (e->ht) && e->parsing) {
1294                str = html_tokenizer_next_token (e->ht);
1295
1296                /* The token parser has pushed a body we want to use it. */
1297                /* CLUECHECK */
1298                clue = e->parser_clue;
1299                /* printf ("%p <-- clue\n", clue); */
1300
1301                if (str == NULL)
1302                        break;
1303
1304                if (*str == '\0')
1305                        continue;
1306
1307                if (*str != TAG_ESCAPE) {
1308                        parse_text (e, clue, str);
1309                } else {
1310                        gint i  = 0;
1311                        str++;
1312
1313                        while (end [i] != 0) {
1314                                if (strncasecmp (str, end[i], strlen(end[i])) == 0) {
1315                                        rv = str;
1316                                }
1317                                i++;
1318                        }
1319                       
1320                        /* The tag used for line break when we are in <pre>...</pre> */
1321                        if (*str == '\n') {
1322                                if (e->inPre)
1323                                        add_line_break (e, clue, HTML_CLEAR_NONE);
1324                                else {
1325                                        char *str_copy = g_strdup (str);
1326                                        *str_copy = ' ';
1327                                        parse_text (e, clue, str_copy);
1328                                        g_free (str_copy);
1329                                       
1330                                }
1331                        } else
1332                                parse_one_token (e, clue, str);
1333                }
1334        }
1335       
1336        if (!html_tokenizer_has_more_tokens (e->ht) && !e->writing)
1337                html_engine_stop_parser (e);
1338
1339        return rv;
1340}
1341
1342static gchar *
1343discard_body (HTMLEngine *p, const gchar *end[])
1344{
1345        gchar *str = NULL;
1346
1347        while (html_tokenizer_has_more_tokens (p->ht) && p->parsing) {
1348                str = html_tokenizer_next_token (p->ht);
1349
1350                if (*str == '\0')
1351                        continue;
1352
1353                if ((*str == ' ' && *(str+1) == '\0')
1354                    || (*str != TAG_ESCAPE)) {
1355                        /* do nothing */
1356                }
1357                else {
1358                        gint i  = 0;
1359                        str++;
1360                       
1361                        while (end [i] != 0) {
1362                                if (strncasecmp (str, end[i], strlen(end[i])) == 0) {
1363                                        return str;
1364                                }
1365                                i++;
1366                        }
1367                }
1368        }
1369
1370        return 0;
1371}
1372
1373static gboolean
1374is_leading_space (guchar *str)
1375{
1376        while (*str != '\0') {
1377                if (!(isspace (*str) || IS_UTF8_NBSP (str)))
1378                        return FALSE;
1379               
1380                str = g_utf8_next_char (str);
1381        }
1382        return TRUE;
1383}
1384
1385static void
1386element_parse_param (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1387{
1388        GtkHTMLEmbedded *eb;
1389        HTMLElement *element;
1390        char *name = NULL, *value = NULL;
1391       
1392        if (html_stack_is_empty (e->embeddedStack))
1393                return;
1394       
1395        eb = html_stack_top (e->embeddedStack);
1396       
1397        element = html_element_new (e, str);
1398
1399        html_element_get_attr (element, "value", &value);
1400        if (html_element_get_attr (element, "name", &name) && name)
1401                gtk_html_embedded_set_parameter(eb, name, value);
1402       
1403        /* no close tag */
1404        html_element_free (element);
1405}
1406
1407static gchar *
1408parse_object_params(HTMLEngine *p, HTMLObject *clue)
1409{
1410        gchar *str;
1411
1412        /* we peek at tokens looking for <param> elements and
1413         * as soon as we find something that is not whitespace or a param
1414         * element we bail and the caller deal with the rest
1415         */
1416        while (html_tokenizer_has_more_tokens (p->ht) && p->parsing) {
1417                str = html_tokenizer_peek_token (p->ht);
1418               
1419                if (*str == '\0' ||
1420                    *str == '\n' ||
1421                    is_leading_space (str)) {
1422                                str = html_tokenizer_next_token (p->ht);
1423                                /* printf ("\"%s\": was the string\n", str); */
1424                                continue;
1425                } else if (*str == TAG_ESCAPE) {
1426                        str++;
1427                        if (strncasecmp ("<param", str, 6) == 0) {
1428                                /* go ahead and remove the token */
1429                                html_tokenizer_next_token (p->ht);
1430
1431                                parse_one_token (p, clue, str);
1432                                continue;
1433                        }
1434                }
1435                return str;
1436        }
1437       
1438        return NULL;   
1439}
1440
1441static void
1442block_end_object (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1443{
1444        if (!html_stack_is_empty (e->embeddedStack)) {
1445                GObject *o = G_OBJECT (html_stack_pop (e->embeddedStack));
1446                g_object_unref (o);
1447        }
1448}
1449
1450static void
1451element_parse_object (HTMLEngine *e, HTMLObject *clue, const gchar *attr)
1452{
1453        char *classid = NULL;
1454        char *name    = NULL;
1455        char *type    = NULL;
1456        char *str     = NULL;
1457        char *data    = NULL;
1458        char *value   = NULL;
1459        int width=-1,height=-1;
1460        static const gchar *end[] = { "</object", 0};
1461        GtkHTMLEmbedded *eb;
1462        HTMLEmbedded *el;
1463        gboolean object_found;
1464        HTMLElement *element;
1465
1466
1467        /* this might have to do something different for form object
1468           elements - check the spec MPZ */
1469       
1470        element = html_element_new (e, attr);
1471
1472        if (html_element_get_attr (element, "classid", &value))
1473                classid = g_strdup (value);
1474               
1475        if (html_element_get_attr (element, "name", &value))
1476                name = g_strdup (value);
1477
1478        if (html_element_get_attr (element, "type", &value))
1479                type = g_strdup (value);
1480
1481        if (html_element_get_attr (element, "data", &value))
1482                data = g_strdup (value);
1483       
1484        if (html_element_get_attr (element, "width", &value))
1485                element->style = html_style_add_width (element->style, value);
1486
1487        if (html_element_get_attr (element, "height", &value))
1488                element->style = html_style_add_height (element->style, value);
1489
1490        element->style = html_style_set_display (element->style, DISPLAY_NONE);
1491        html_element_parse_coreattrs (element);
1492
1493        if (element->style->width)
1494                width = element->style->width->val;
1495
1496        if (element->style->height)
1497                width = element->style->height->val;
1498
1499        eb = (GtkHTMLEmbedded *) gtk_html_embedded_new (classid, name, type, data,
1500                                                        width, height);
1501
1502        html_stack_push (e->embeddedStack, eb);
1503        g_object_ref (eb);
1504        el = html_embedded_new_widget (GTK_WIDGET (e->widget), eb, e);
1505
1506        /* evaluate params */
1507        parse_object_params (e, clue);
1508
1509        /* create the object */
1510        object_found = FALSE;
1511        printf ("requesting object classid: %s\n", classid ? classid : "(null)");
1512        g_signal_emit (e, signals [OBJECT_REQUESTED], 0, eb, &object_found);
1513        printf ("object_found: %d\n", object_found);
1514       
1515        /* show alt text on TRUE */
1516        if (object_found) {
1517                append_element (e, clue, HTML_OBJECT(el));
1518                /* automatically add this to a form if it is part of one */
1519                if (e->form)
1520                        html_form_add_element (e->form, HTML_EMBEDDED (el));
1521
1522                /* throw away the contents we can deal with the object */
1523                str = discard_body (e, end);
1524        } else {
1525                html_object_destroy (HTML_OBJECT (el));
1526        }
1527       
1528        push_block (e, "object", DISPLAY_NONE, block_end_object, FALSE, FALSE);
1529
1530        g_free (type);
1531        g_free (data);
1532        g_free (classid);
1533        g_free (name);
1534}       
1535
1536
1537/* Frame parsers */
1538static void
1539element_parse_noframe (HTMLEngine *e, HTMLObject *clue, const gchar *str )
1540{
1541        static const char *end[] = {"</noframe", NULL};
1542       
1543        if (e->allow_frameset)
1544                discard_body (e, end);
1545}
1546
1547static void
1548block_end_frameset (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1549{
1550        if (!html_stack_is_empty (e->frame_stack))
1551                html_stack_pop (e->frame_stack);
1552}
1553
1554static void
1555element_parse_frameset (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1556{
1557        HTMLElement *element;
1558        HTMLObject *set;
1559        char *value = NULL;
1560        char *rows  = NULL;
1561        char *cols  = NULL;
1562
1563        if (e->allow_frameset)
1564                return;
1565
1566        element = html_element_new (e, str);
1567
1568        if (html_element_get_attr (element, "rows", &value))
1569                rows = value;
1570
1571        if (html_element_get_attr (element, "cols", &value))
1572                cols = value;
1573
1574        /*
1575        html_element_get_attr (element, "onload", &value);
1576        html_element_get_attr (element, "onunload", &value);
1577        */
1578
1579        /* clear the borders */
1580        e->bottomBorder = 0;
1581        e->topBorder = 0;
1582        e->leftBorder = 0;
1583        e->rightBorder = 0;
1584       
1585        set = html_frameset_new (e->widget, rows, cols);
1586
1587        if (html_stack_is_empty (e->frame_stack)) {
1588                append_element (e, clue, set);
1589        } else {
1590                html_frameset_append (html_stack_top (e->frame_stack), set);
1591        }
1592               
1593        html_stack_push (e->frame_stack, set);
1594        push_block (e, "frameset", DISPLAY_NONE, block_end_frameset, 0, 0);
1595}
1596
1597static void
1598element_parse_iframe (HTMLEngine *e, HTMLObject *clue, const char *str)
1599{
1600        HTMLElement *element;
1601        char *value = NULL;
1602        char *src   = NULL;
1603        HTMLObject *iframe;
1604        static const gchar *end[] = { "</iframe", 0};
1605        gint width           = -1;
1606        gint height          = -1;
1607        gint border          = TRUE;
1608        GtkPolicyType scroll = GTK_POLICY_AUTOMATIC;
1609        gint margin_width    = -1;
1610        gint margin_height   = -1;
1611        HTMLHAlignType halign = HTML_HALIGN_NONE;
1612        HTMLVAlignType valign = HTML_VALIGN_NONE;
1613
1614        element = html_element_new (e, str);
1615
1616        if (html_element_get_attr (element, "src", &value))
1617                src = value;
1618
1619        if (html_element_get_attr (element, "height", &value))
1620                element->style = html_style_add_height (element->style, value);
1621
1622        if (html_element_get_attr (element, "width", &value))
1623                element->style = html_style_add_width (element->style, value);
1624               
1625        if (html_element_get_attr (element, "scrolling", &value))
1626                scroll = parse_scroll (value);
1627
1628        if (html_element_get_attr (element, "marginwidth", &value))
1629                margin_width = atoi (value);
1630
1631        if (html_element_get_attr (element, "marginheight", &value))
1632                margin_height = atoi (value);
1633
1634        if (html_element_get_attr (element, "frameborder", &value))
1635                border = atoi (value);
1636
1637        if (html_element_get_attr (element, "align", &value)) {
1638                if (strcasecmp ("left", value) == 0)
1639                        halign = HTML_HALIGN_LEFT;
1640                else if (strcasecmp ("right", value) == 0)
1641                        halign = HTML_HALIGN_RIGHT;
1642                else if (strcasecmp ("top", value) == 0)
1643                        valign = HTML_VALIGN_TOP;
1644                else if (strcasecmp ("middle", value) == 0)
1645                        valign = HTML_VALIGN_MIDDLE;
1646                else if (strcasecmp ("bottom", value) == 0)
1647                        valign = HTML_VALIGN_BOTTOM;
1648        }
1649        element->style = html_style_set_display (element->style, DISPLAY_NONE);
1650        /*
1651        html_element_get_attr (element, "longdesc", &value);
1652        html_element_get_attr (element, "name", &value);
1653        */
1654               
1655        /* FIXME fixup missing url */
1656        if (src) {
1657                if (element->style->width)
1658                        width = element->style->width->val;
1659
1660                if (element->style->height)
1661                        width = element->style->height->val;
1662               
1663                iframe = html_iframe_new (GTK_WIDGET (e->widget), src, width, height, border);
1664                if (margin_height >= 0)
1665                        html_iframe_set_margin_height (HTML_IFRAME (iframe), margin_height);
1666                if (margin_width >= 0)
1667                        html_iframe_set_margin_width (HTML_IFRAME (iframe), margin_width);
1668                if (scroll != GTK_POLICY_AUTOMATIC)
1669                        html_iframe_set_scrolling (HTML_IFRAME (iframe), scroll);
1670               
1671                if (halign != HTML_HALIGN_NONE) {
1672                        HTMLClueAligned *aligned = HTML_CLUEALIGNED (html_cluealigned_new (NULL, 0, 0, clue->max_width, 100));
1673                        HTML_CLUE (aligned)->halign = halign;
1674                        html_clue_append (HTML_CLUE (aligned), HTML_OBJECT (iframe));
1675                        append_element (e, clue, HTML_OBJECT (aligned));
1676                } else {
1677                        append_element (e, clue, iframe);
1678                }
1679                discard_body (e, end);
1680        }
1681 
1682        html_element_free (element);
1683}
1684
1685
1686static void
1687element_parse_area (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1688{
1689        HTMLShape *shape;
1690        char *type = NULL;
1691        char *href = NULL;
1692        char *coords = NULL;
1693        char *target = NULL;
1694       
1695        if (e->map == NULL)
1696                return;
1697       
1698        html_string_tokenizer_tokenize (e->st, str + 5, " >");
1699        while (html_string_tokenizer_has_more_tokens (e->st)) {   
1700                gchar *token = html_string_tokenizer_next_token (e->st);
1701               
1702                if (strncasecmp (token, "shape=", 6) == 0) {
1703                        type = g_strdup (token + 6);
1704                } else if (strncasecmp (token, "href=", 5) == 0) {
1705                        href = g_strdup (token +5);
1706                } else if ( strncasecmp (token, "target=", 7) == 0) {
1707                        target = g_strdup (token + 7);
1708                } else if ( strncasecmp (token, "coords=", 7) == 0) {
1709                        coords = g_strdup (token + 7);
1710                }
1711        }
1712       
1713        if (type || coords) {
1714               
1715                shape = html_shape_new (type, coords, href, target);
1716                if (shape != NULL) {
1717                        html_map_add_shape (e->map, shape);
1718                }
1719        }
1720       
1721        g_free (type);
1722        g_free (href);
1723        g_free (coords);
1724        g_free (target);
1725}
1726
1727static void
1728block_end_anchor (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1729{
1730        g_free (e->url);
1731        e->url = NULL;
1732
1733        g_free (e->target);
1734        e->target = NULL;
1735
1736        e->eat_space = FALSE;
1737}
1738
1739static void
1740element_parse_a (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1741{
1742        HTMLElement *element;
1743        gchar *url = NULL;
1744        gchar *id = NULL;
1745        char *type = NULL;
1746        char *coords = NULL;
1747        char *target = NULL;
1748        char *value;
1749       
1750        pop_element (e, ID_A);
1751       
1752        element = html_element_new (e, str);
1753        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
1754       
1755        if (html_element_get_attr (element, "href", &value)) {
1756                url = g_strdup (value);
1757
1758                g_free (e->url);
1759                e->url = url;
1760        }
1761       
1762        if (html_element_get_attr (element, "target", &value))
1763                target = g_strdup (value);
1764
1765        if (html_element_get_attr (element, "id", &value))
1766                id = g_strdup (value);
1767
1768        if (id == NULL && html_element_get_attr (element, "name", &value))
1769                id = g_strdup (value);
1770       
1771        if (html_element_get_attr (element, "shape", &type)
1772            || html_element_get_attr (element, "coords", &coords)) {
1773                HTMLShape *shape;
1774               
1775                shape = html_shape_new (type, coords, url, target);
1776                if (shape)
1777                        html_map_add_shape (e->map, shape);
1778        }
1779
1780        if (id != NULL) {
1781                if (e->flow == NULL)
1782                        html_clue_append (HTML_CLUE (clue),
1783                                          html_anchor_new (id));
1784                else
1785                        html_clue_append (HTML_CLUE (e->flow),
1786                                          html_anchor_new (id));
1787                g_free (id);
1788        }
1789       
1790        html_element_parse_coreattrs (element);
1791       
1792        element->exitFunc = block_end_anchor;
1793        html_element_push (element, e, clue);
1794}
1795
1796
1797/* block parsing */
1798static void
1799block_end_clueflow_style (HTMLEngine *e,
1800                          HTMLObject *clue,
1801                          HTMLElement *elem)
1802{
1803        finish_flow (e, clue);
1804        pop_clueflow_style (e);
1805}
1806
1807static void
1808element_parse_address (HTMLEngine *e, HTMLObject *clue, const char *str)
1809{
1810        HTMLStyle *style = NULL;
1811       
1812        style = html_style_set_decoration (style, GTK_HTML_FONT_STYLE_ITALIC);
1813        push_block_element (e, ID_ADDRESS, style, DISPLAY_BLOCK, block_end_clueflow_style, 0, 0);
1814       
1815        push_clueflow_style (e, HTML_CLUEFLOW_STYLE_ADDRESS);
1816        close_flow (e, clue);
1817
1818        e->avoid_para = TRUE;
1819}
1820
1821static void
1822block_end_pre (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
1823{
1824        block_end_clueflow_style (e, clue, elem);
1825
1826        finish_flow (e, clue);
1827
1828        e->inPre--;
1829}
1830
1831static void
1832element_parse_pre (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1833{
1834        push_block (e, ID_PRE, DISPLAY_BLOCK, block_end_pre, 0, 0);
1835
1836        push_clueflow_style (e, HTML_CLUEFLOW_STYLE_PRE);
1837        finish_flow (e, clue);
1838
1839        e->inPre++;
1840        e->avoid_para = TRUE;
1841}
1842
1843static void
1844element_parse_center (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1845{
1846        HTMLElement *element;
1847
1848        element = html_element_new (e, str);
1849       
1850        element->style = html_style_set_display (element->style, DISPLAY_BLOCK);
1851        element->style = html_style_add_text_align (element->style, HTML_HALIGN_CENTER);
1852
1853        html_element_parse_coreattrs (element);
1854        html_element_push (element, e, clue);
1855}
1856
1857static void
1858element_parse_div (HTMLEngine *e, HTMLObject *clue, const char *str)
1859{
1860        HTMLElement *element;
1861        char *value;
1862
1863        element = html_element_new (e, str);
1864
1865        element->style = html_style_set_display (element->style, DISPLAY_BLOCK);
1866
1867        if (html_element_get_attr (element, "align", &value))
1868                element->style = html_style_add_text_align (element->style, parse_halign (value, HTML_HALIGN_NONE));
1869       
1870        html_element_parse_coreattrs (element);
1871        html_element_push (element, e, clue);
1872}
1873
1874static void
1875element_parse_p (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1876{
1877        if (*str != '/') {
1878                HTMLStyle *style = NULL;
1879                gchar *class = NULL;
1880                gchar *token;
1881
1882                html_string_tokenizer_tokenize (e->st, (gchar *)(str + 2), " >");
1883                while (html_string_tokenizer_has_more_tokens (e->st)) {
1884                        token = html_string_tokenizer_next_token (e->st);
1885                        if (strncasecmp (token, "align=", 6) == 0) {
1886                                style = html_style_add_text_align (style, parse_halign (token + 6, HTML_HALIGN_NONE));
1887                        } else if (strncasecmp (token, "class=", 6) == 0) {
1888                                class = g_strdup (token + 6);
1889                        }
1890                }
1891               
1892                push_block_element (e, ID_P, style, DISPLAY_BLOCK, block_end_p, 0, 0);
1893                if (!e->avoid_para) {   
1894                        new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1895                        new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1896                } else {
1897#if 1
1898                        update_flow_align (e, clue);
1899#else
1900                        if (e->flow)
1901                                HTML_CLUE (e->flow)->halign = current_alignment (e);
1902                        else
1903                                new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1904
1905#endif
1906                }
1907                g_free (class);
1908
1909                e->avoid_para = TRUE;
1910        } else {
1911                pop_element (e, ID_P);
1912                if (!e->avoid_para) {
1913                        new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1914                        new_flow (e, clue, NULL, HTML_CLEAR_NONE);
1915                        e->avoid_para = TRUE;
1916                }               
1917        }
1918}
1919
1920static void
1921element_parse_br (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1922{
1923        HTMLClearType clear;
1924       
1925        clear = HTML_CLEAR_NONE;
1926
1927        /*
1928         * FIXME this parses the clear attributes on close tags
1929         * as well I'm not sure if we should do that or not, someone
1930         * should check the mozilla behavior
1931         */
1932        html_string_tokenizer_tokenize (e->st, str + 3, " >");
1933        while (html_string_tokenizer_has_more_tokens (e->st)) {
1934                gchar *token = html_string_tokenizer_next_token (e->st);
1935               
1936                if (strncasecmp (token, "clear=", 6) == 0) {
1937                        gtk_html_debug_log (e->widget, "%s\n", token);
1938                        if (strncasecmp (token + 6, "left", 4) == 0)
1939                                clear = HTML_CLEAR_LEFT;
1940                        else if (strncasecmp (token + 6, "right", 5) == 0)
1941                                clear = HTML_CLEAR_RIGHT;
1942                        else if (strncasecmp (token + 6, "all", 3) == 0)
1943                                clear = HTML_CLEAR_ALL;
1944                }
1945        }
1946       
1947        add_line_break (e, clue, clear);
1948}
1949
1950
1951static void
1952element_parse_body (HTMLEngine *e, HTMLObject *clue, const gchar *str)
1953{
1954        GdkColor color;
1955
1956        html_string_tokenizer_tokenize (e->st, str + 5, " >");
1957        while (html_string_tokenizer_has_more_tokens (e->st)) {
1958                gchar *token;
1959               
1960                token = html_string_tokenizer_next_token (e->st);
1961                gtk_html_debug_log (e->widget, "token is: %s\n", token);
1962               
1963                if (strncasecmp (token, "bgcolor=", 8) == 0) {
1964                        gtk_html_debug_log (e->widget, "setting color\n");
1965                        if (html_parse_color (token + 8, &color)) {
1966                                gtk_html_debug_log (e->widget, "bgcolor is set\n");
1967                                html_colorset_set_color (e->settings->color_set, &color, HTMLBgColor);
1968                        } else {
1969                                gtk_html_debug_log (e->widget, "Color `%s' could not be parsed\n", token);
1970                        }
1971                } else if (strncasecmp (token, "background=", 11) == 0
1972                           && token [12]
1973                           && ! e->defaultSettings->forceDefault) {
1974                        gchar *bgurl;
1975                       
1976                        bgurl = g_strdup (token + 11);
1977                        if (e->bgPixmapPtr != NULL)
1978                                html_image_factory_unregister(e->image_factory, e->bgPixmapPtr, NULL);
1979                        e->bgPixmapPtr = html_image_factory_register(e->image_factory, NULL, bgurl, FALSE);
1980                        g_free (bgurl);
1981                } else if ( strncasecmp( token, "text=", 5 ) == 0
1982                            && !e->defaultSettings->forceDefault ) {
1983                        if (html_parse_color (token + 5, &color)) {
1984                                html_colorset_set_color (e->settings->color_set, &color, HTMLTextColor);
1985                                push_element (e, ID_BODY, NULL,
1986                                              html_style_add_color (NULL, html_colorset_get_color (e->settings->color_set, HTMLTextColor)));
1987                        }
1988                } else if ( strncasecmp( token, "link=", 5 ) == 0
1989                            && !e->defaultSettings->forceDefault ) {
1990                        html_parse_color (token + 5, &color);
1991                        html_colorset_set_color (e->settings->color_set, &color, HTMLLinkColor);
1992                } else if ( strncasecmp( token, "vlink=", 6 ) == 0
1993                            && !e->defaultSettings->forceDefault ) {
1994                        html_parse_color (token + 6, &color);
1995                        html_colorset_set_color (e->settings->color_set, &color, HTMLVLinkColor);
1996                } else if ( strncasecmp( token, "alink=", 6 ) == 0
1997                            && !e->defaultSettings->forceDefault ) {
1998                        html_parse_color (token + 6, &color);
1999                        html_colorset_set_color (e->settings->color_set, &color, HTMLALinkColor);
2000                } else if ( strncasecmp( token, "leftmargin=", 11 ) == 0) {
2001                        e->leftBorder = atoi (token + 11);
2002                } else if ( strncasecmp( token, "rightmargin=", 12 ) == 0) {
2003                        e->rightBorder = atoi (token + 12);
2004                } else if ( strncasecmp( token, "topmargin=", 10 ) == 0) {
2005                        e->topBorder = atoi (token + 10);
2006                } else if ( strncasecmp( token, "bottommargin=", 13 ) == 0) {
2007                        e->bottomBorder = atoi (token + 13);
2008                } else if ( strncasecmp( token, "marginwidth=", 12 ) == 0) {
2009                        e->leftBorder = e->rightBorder = atoi (token + 12);
2010                } else if ( strncasecmp( token, "marginheight=", 13 ) == 0) {
2011                        e->topBorder = e->bottomBorder = atoi (token + 13);
2012                }
2013        }
2014       
2015        gtk_html_debug_log (e->widget, "parsed <body>\n");
2016}
2017
2018static void
2019element_parse_base (HTMLEngine *e, HTMLObject *clue, const char *str)
2020{
2021        html_string_tokenizer_tokenize( e->st, str + 5, " >" );
2022        while ( html_string_tokenizer_has_more_tokens (e->st) ) {
2023                const char* token = html_string_tokenizer_next_token(e->st);
2024                if ( strncasecmp( token, "target=", 7 ) == 0 ) {
2025                        g_signal_emit (e, signals [SET_BASE_TARGET], 0, token + 7);
2026                } else if ( strncasecmp( token, "href=", 5 ) == 0 ) {
2027                        g_signal_emit (e, signals [SET_BASE], 0, token + 5);
2028                }
2029        }
2030}
2031
2032
2033static void
2034element_parse_data (HTMLEngine *e, HTMLObject *clue, const char *str)
2035{
2036        gchar *key = NULL;
2037        gchar *class_name = NULL;
2038       
2039        html_string_tokenizer_tokenize (e->st, str + 5, " >" );
2040        while (html_string_tokenizer_has_more_tokens (e->st)) {
2041                const gchar *token = html_string_tokenizer_next_token (e->st);
2042                if (strncasecmp (token, "class=", 6 ) == 0) {
2043                        g_free (class_name);
2044                        class_name = g_strdup (token + 6);
2045                } else if (strncasecmp (token, "key=", 4 ) == 0) {
2046                        g_free (key);
2047                        key = g_strdup (token + 4);
2048                } else if (class_name && key && strncasecmp (token, "value=", 6) == 0) {
2049                        if (class_name) {
2050                                html_engine_set_class_data (e, class_name, key, token + 6);
2051                                if (!strcmp (class_name, "ClueFlow") && e->flow)
2052                                        html_engine_set_object_data (e, e->flow);
2053                        }
2054                } else if (strncasecmp (token, "clear=", 6) == 0)
2055                        if (class_name)
2056                                html_engine_clear_class_data (e, class_name, token + 6);
2057                /* TODO clear flow data */
2058        }
2059        g_free (class_name);
2060        g_free (key);
2061}
2062
2063
2064static void
2065form_begin (HTMLEngine *e, HTMLObject *clue, gchar *action, gchar *method, gboolean close_paragraph)
2066{
2067        e->form = html_form_new (e, action, method);
2068        e->formList = g_list_append (e->formList, e->form);
2069
2070        if (! e->avoid_para && close_paragraph) {
2071                if (e->flow && HTML_CLUE (e->flow)->head)
2072                        close_flow (e, clue);
2073                e->avoid_para = FALSE;
2074        }
2075}
2076
2077static void
2078block_end_form (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2079{
2080        e->form = NULL;
2081
2082        if (!e->avoid_para && elem && elem->miscData1) {
2083                close_flow (e, clue);
2084        }
2085}
2086
2087static void
2088element_parse_input (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2089{
2090        enum InputType { CheckBox, Hidden, Radio, Reset, Submit, Text, Image,
2091                         Button, Password, Undefined };
2092        HTMLObject *element = NULL;
2093        const char *p;
2094        enum InputType type = Text;
2095        gchar *name = NULL;
2096        gchar *value = NULL;
2097        gchar *imgSrc = NULL;
2098        gboolean checked = FALSE;
2099        int size = 20;
2100        int maxLen = -1;
2101        int imgHSpace = 0;
2102        int imgVSpace = 0;
2103        gboolean fix_form = FALSE;
2104       
2105        if (e->form == NULL) {
2106                fix_form = TRUE;
2107                form_begin (e, clue, NULL, "GET", FALSE);
2108        }               
2109
2110        html_string_tokenizer_tokenize (e->st, str + 6, " >");
2111        while (html_string_tokenizer_has_more_tokens (e->st)) {
2112                const gchar *token = html_string_tokenizer_next_token (e->st);
2113
2114                if ( strncasecmp( token, "type=", 5 ) == 0 ) {
2115                        p = token + 5;
2116                        if ( strncasecmp( p, "checkbox", 8 ) == 0 )
2117                                type = CheckBox;
2118                        else if ( strncasecmp( p, "password", 8 ) == 0 )
2119                                type = Password;
2120                        else if ( strncasecmp( p, "hidden", 6 ) == 0 )
2121                                type = Hidden;
2122                        else if ( strncasecmp( p, "radio", 5 ) == 0 )
2123                                type = Radio;
2124                        else if ( strncasecmp( p, "reset", 5 ) == 0 )
2125                                type = Reset;
2126                        else if ( strncasecmp( p, "submit", 5 ) == 0 )
2127                                type = Submit;
2128                        else if ( strncasecmp( p, "button", 6 ) == 0 )
2129                                type = Button;
2130                        else if ( strncasecmp( p, "text", 5 ) == 0 )
2131                                type = Text;
2132                        else if ( strncasecmp( p, "image", 5 ) == 0 )
2133                                type = Image;
2134                }
2135                else if ( strncasecmp( token, "name=", 5 ) == 0 ) {
2136                        name = g_strdup(token + 5);
2137                }
2138                else if ( strncasecmp( token, "value=", 6 ) == 0 ) {
2139                        value = g_strdup(token + 6);
2140                }
2141                else if ( strncasecmp( token, "size=", 5 ) == 0 ) {
2142                        size = atoi( token + 5 );
2143                }
2144                else if ( strncasecmp( token, "maxlength=", 10 ) == 0 ) {
2145                        maxLen = atoi( token + 10 );
2146                }
2147                else if ( strncasecmp( token, "checked", 7 ) == 0 ) {
2148                        checked = TRUE;
2149                }
2150                else if ( strncasecmp( token, "src=", 4 ) == 0 ) {
2151                        imgSrc = g_strdup (token + 4);
2152                }
2153                else if ( strncasecmp( token, "onClick=", 8 ) == 0 ) {
2154                        /* TODO: Implement Javascript */
2155                }
2156                else if ( strncasecmp( token, "hspace=", 7 ) == 0 ) {
2157                        imgHSpace = atoi (token + 7);
2158                }
2159                else if ( strncasecmp( token, "vspace=", 7 ) == 0 ) {
2160                        imgVSpace = atoi (token + 7);
2161                }
2162        }
2163        switch ( type ) {
2164        case CheckBox:
2165                element = html_checkbox_new(GTK_WIDGET(e->widget), name, value, checked);
2166                break;
2167        case Hidden:
2168                {
2169                HTMLObject *hidden = html_hidden_new(name, value);
2170
2171                html_form_add_hidden (e->form, HTML_HIDDEN (hidden));
2172
2173                break;
2174                }
2175        case Radio:
2176                element = html_radio_new(GTK_WIDGET(e->widget), name, value, checked, e->form);
2177                break;
2178        case Reset:
2179                element = html_button_new(GTK_WIDGET(e->widget), name, value, BUTTON_RESET);
2180                break;
2181        case Submit:
2182                element = html_button_new(GTK_WIDGET(e->widget), name, value, BUTTON_SUBMIT);
2183                break;
2184        case Button:
2185                element = html_button_new(GTK_WIDGET(e->widget), name, value, BUTTON_NORMAL);
2186                break;
2187        case Text:
2188        case Password:
2189                element = html_text_input_new(GTK_WIDGET(e->widget), name, value, size, maxLen, (type == Password));
2190                break;
2191        case Image:
2192                /* FIXME fixup missing url */
2193                if (imgSrc) {
2194                        element = html_imageinput_new (e->image_factory, name, imgSrc);
2195                        html_image_set_spacing (HTML_IMAGE (HTML_IMAGEINPUT (element)->image), imgHSpace, imgVSpace);
2196                }
2197                break;
2198        case Undefined:
2199                g_warning ("Unknown <input type>\n");
2200                break;
2201        }
2202        if (element) {
2203                append_element (e, clue, element);
2204                html_form_add_element (e->form, HTML_EMBEDDED (element));
2205        }
2206
2207        if (name)
2208                g_free (name);
2209        if (value)
2210                g_free (value);
2211        if (imgSrc)
2212                g_free (imgSrc);
2213       
2214        if (fix_form)
2215                block_end_form (e, clue, NULL);
2216}
2217
2218static void
2219element_parse_form (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2220{
2221        gchar *action = NULL;
2222        gchar *method = "GET";
2223        gchar *target = NULL;
2224       
2225        html_string_tokenizer_tokenize (e->st, str + 5, " >");
2226        while (html_string_tokenizer_has_more_tokens (e->st)) {
2227                const gchar *token = html_string_tokenizer_next_token (e->st);
2228               
2229                if ( strncasecmp( token, "action=", 7 ) == 0 ) {
2230                        action = g_strdup (token + 7);
2231                } else if ( strncasecmp( token, "method=", 7 ) == 0 ) {
2232                        if ( strncasecmp( token + 7, "post", 4 ) == 0 )
2233                                method = "POST";
2234                } else if ( strncasecmp( token, "target=", 7 ) == 0 ) {
2235                        target = g_strdup(token + 7);
2236                }
2237        }
2238       
2239        form_begin (e, clue, action, method, TRUE);
2240        g_free(action);
2241        g_free(target);
2242       
2243        push_block (e, ID_FORM, DISPLAY_BLOCK, block_end_form, TRUE, 0);
2244}
2245
2246static void
2247element_parse_frame (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2248{
2249        HTMLElement *element;
2250        char *value = NULL;
2251        char *src = NULL;
2252        HTMLObject *frame = NULL;
2253        gint margin_height = -1;
2254        gint margin_width = -1;
2255        GtkPolicyType scroll = GTK_POLICY_AUTOMATIC;
2256       
2257        if (!e->allow_frameset)
2258                        return;
2259       
2260        src = NULL;
2261
2262        element = html_element_new (e, str);
2263
2264        if (html_element_get_attr (element, "src", &value))
2265                src = value;
2266
2267        if (html_element_get_attr (element, "marginheight", &value))
2268                margin_height = atoi (value);
2269
2270        if (html_element_get_attr (element, "marginwidth", &value))
2271                margin_width = atoi (value);
2272
2273        if (html_element_get_attr (element, "scrolling", &value))
2274                scroll = parse_scroll (value);
2275
2276#if 0
2277        if (html_element_has_attr (element, "noresize"))
2278                ;
2279
2280        if (html_element_get_attr (element, "frameborder", &value))
2281                ;
2282               
2283        /*
2284         * Netscape and Mozilla recognize this to turn of all the between
2285         * frame decoration.
2286         */
2287        if (html_element_get_attr (element, "border", &value))
2288                ;
2289#endif
2290       
2291        frame = html_frame_new (GTK_WIDGET (e->widget), src, -1 , -1, FALSE);
2292        if (!html_frameset_append (html_stack_top (e->frame_stack), frame))
2293                html_object_destroy (frame);
2294       
2295        if (margin_height > 0)
2296                html_frame_set_margin_height (HTML_FRAME (frame), margin_height);
2297        if (margin_width > 0)
2298                html_frame_set_margin_width (HTML_FRAME (frame), margin_width);
2299        if (scroll != GTK_POLICY_AUTOMATIC)
2300                html_frame_set_scrolling (HTML_FRAME (frame), scroll);
2301       
2302       
2303        html_element_free (element);
2304}
2305
2306
2307static void
2308element_parse_hr (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2309{
2310        HTMLElement *element;
2311        gint size = 2;
2312        gint length = clue->max_width;
2313        gint percent = 100;
2314        HTMLHAlignType align = HTML_HALIGN_CENTER;
2315        gboolean shade = TRUE;
2316        char *value;
2317        HTMLLength *len;
2318       
2319
2320        element = html_element_new (e, str);
2321
2322        if (html_element_get_attr (element, "align", &value))
2323                align = parse_halign (value, align);
2324       
2325        if (html_element_get_attr (element, "size", &value))
2326                element->style = html_style_add_height (element->style, value);
2327       
2328        if (html_element_get_attr (element,"width", &value))
2329                element->style = html_style_add_width (element->style, value);
2330
2331        if (html_element_has_attr (element, "noshade"))
2332                shade = FALSE;
2333       
2334        html_element_parse_coreattrs (element);
2335        element->style = html_style_set_display (element->style, DISPLAY_NONE);
2336
2337        pop_element (e, ID_P);
2338        len = element->style->width;
2339        if (len) {
2340                if (len->type == HTML_LENGTH_TYPE_PERCENT) {
2341                        percent = len->val;
2342                        length = 0;
2343                } else {
2344                        percent = 0;
2345                        length = len->val;
2346                }
2347        }
2348
2349        len = element->style->height;
2350        if (len)
2351                size = len->val;
2352               
2353
2354        append_element (e, clue, html_rule_new (length, percent, size, shade, align));
2355        close_flow (e, clue);
2356
2357        /* no close tag */
2358        html_element_free (element);
2359}
2360
2361
2362static void
2363block_end_heading (HTMLEngine *e,
2364                   HTMLObject *clue,
2365                   HTMLElement *elem)
2366{
2367        block_end_clueflow_style (e, clue, elem);
2368
2369        e->avoid_para = TRUE;
2370}
2371
2372static void
2373element_end_heading (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2374{
2375        pop_element (e, "h1");
2376        pop_element (e, "h2");
2377        pop_element (e, "h3");
2378        pop_element (e, "h4");
2379        pop_element (e, "h5");
2380        pop_element (e, "h6");
2381}
2382
2383static void
2384element_parse_heading (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2385{
2386        HTMLClueFlowStyle fstyle;
2387        HTMLStyle *style = NULL;
2388       
2389        element_end_heading (e, clue, str);
2390
2391        fstyle = HTML_CLUEFLOW_STYLE_H1 + (str[1] - '1');
2392        style = html_style_set_decoration (style, GTK_HTML_FONT_STYLE_BOLD);
2393        switch (fstyle) {
2394        case HTML_CLUEFLOW_STYLE_H6:
2395                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_1);
2396                break;
2397        case HTML_CLUEFLOW_STYLE_H5:
2398                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_2);
2399                break;
2400        case HTML_CLUEFLOW_STYLE_H4:
2401                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_3);
2402                break;
2403        case HTML_CLUEFLOW_STYLE_H3:
2404                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_4);
2405                break;
2406        case HTML_CLUEFLOW_STYLE_H2:
2407                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_5);
2408                break;
2409        case HTML_CLUEFLOW_STYLE_H1:
2410                html_style_set_font_size (style, GTK_HTML_FONT_STYLE_SIZE_6);
2411                break;
2412        default:
2413                break;
2414        }
2415       
2416        html_string_tokenizer_tokenize (e->st, str + 3, " >");
2417        while (html_string_tokenizer_has_more_tokens (e->st)) {
2418                gchar *token;
2419               
2420                token = html_string_tokenizer_next_token (e->st);
2421                if (strncasecmp (token, "align=", 6) == 0) {
2422                        style = html_style_add_text_align (style, parse_halign (token + 6, HTML_HALIGN_NONE));
2423                        //align = parse_halign (token + 6, align);
2424                } else if (strncasecmp (token, "style=", 6) == 0) {
2425                        style = html_style_add_attribute (style, token + 6);
2426                }
2427        }
2428       
2429        /* FIXME this is temporary until the paring can be moved.*/
2430        {
2431                char *name = parse_element_name (str);
2432                push_block_element (e, name, style, DISPLAY_BLOCK, block_end_heading, 0, 0);
2433                g_free (name);
2434        }
2435        push_clueflow_style (e, fstyle);       
2436        close_flow (e, clue);
2437
2438        e->avoid_para = TRUE;
2439}
2440
2441static void
2442element_parse_img (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2443{
2444        HTMLElement *element;
2445        HTMLObject *image = 0;
2446        HTMLHAlignType align = HTML_HALIGN_NONE;
2447        HTMLVAlignType valign = HTML_VALIGN_NONE;
2448        HTMLColor *color = NULL;
2449        gchar *value   = NULL;
2450        gchar *tmpurl  = NULL;
2451        gchar *mapname = NULL;
2452        gchar *id      = NULL;
2453        gchar *alt     = NULL;
2454        gint width     = -1;
2455        gint height    = -1;
2456        gint border    = 0;
2457        gint hspace = 0;
2458        gint vspace = 0;
2459        gboolean percent_width  = FALSE;
2460        gboolean percent_height = FALSE;
2461        gboolean ismap = FALSE;
2462
2463        color        = current_color (e);
2464        if (e->url != NULL || e->target != NULL)
2465                border = 2;
2466
2467        if (e->url != NULL || e->target != NULL)
2468                border = 2;
2469       
2470        element = html_element_new (e, str);
2471       
2472        if (html_element_get_attr (element, "src", &value))
2473                tmpurl = value;
2474
2475        if (html_element_get_attr (element, "width", &value))
2476                element->style = html_style_add_width (element->style, value);
2477
2478        if (html_element_get_attr (element, "height", &value))
2479                element->style = html_style_add_height (element->style, value);
2480
2481        if (html_element_get_attr (element, "border", &value))
2482                border = atoi (value);
2483
2484        if (html_element_get_attr (element, "hspace", &value))
2485                hspace = atoi (value);
2486       
2487        if (html_element_get_attr (element, "align", &value)) {
2488                if (strcasecmp ("left", value) == 0)
2489                        align = HTML_HALIGN_LEFT;
2490                else if (strcasecmp ("right", value) == 0)
2491                        align = HTML_HALIGN_RIGHT;
2492                else if (strcasecmp ("top", value) == 0)
2493                        valign = HTML_VALIGN_TOP;
2494                else if (strcasecmp ("middle", value) == 0)
2495                        valign = HTML_VALIGN_MIDDLE;
2496                else if (strcasecmp ("bottom", value) == 0)
2497                        valign = HTML_VALIGN_BOTTOM;
2498        }
2499        if (html_element_get_attr (element, "id", &value))
2500                id = value;
2501
2502        if (html_element_get_attr (element, "alt", &value))
2503                alt = value;
2504
2505        if (html_element_get_attr (element, "usemap", &value))
2506                mapname = value;
2507
2508        if (html_element_has_attr (element, "ismap"))
2509                ismap = TRUE;
2510       
2511        html_element_parse_coreattrs (element);
2512        element->style = html_style_set_display (element->style, DISPLAY_NONE);
2513
2514        /* FIXME fixup missing url */
2515        if (!tmpurl)
2516                return;
2517       
2518        if (align != HTML_HALIGN_NONE)
2519                valign = HTML_VALIGN_BOTTOM;
2520        else if (valign == HTML_VALIGN_NONE)
2521                valign = HTML_VALIGN_BOTTOM;
2522       
2523        if (element->style->width) {
2524                width = element->style->width->val;
2525                percent_width = element->style->width->type == HTML_LENGTH_TYPE_PERCENT;
2526        }
2527
2528        if (element->style->height) {
2529                height = element->style->height->val;
2530                percent_height = element->style->height->type == HTML_LENGTH_TYPE_PERCENT;
2531        }
2532
2533        image = html_image_new (html_engine_get_image_factory (e), tmpurl,
2534                                e->url, e->target,
2535                                width, height,
2536                                percent_width, percent_height, border, color, valign, FALSE);
2537       
2538        if (id)
2539                html_engine_add_object_with_id (e, id, (HTMLObject *) image);
2540       
2541       
2542        if (hspace < 0)
2543                hspace = 0;
2544        if (vspace < 0)
2545                vspace = 0;
2546       
2547        html_image_set_spacing (HTML_IMAGE (image), hspace, vspace);
2548       
2549        if (alt)
2550                html_image_set_alt (HTML_IMAGE (image), alt);
2551       
2552        html_image_set_map (HTML_IMAGE (image), mapname, ismap);
2553       
2554        if (align == HTML_HALIGN_NONE) {
2555                append_element (e, clue, image);
2556                e->eat_space = FALSE;
2557        } else {
2558                /* We need to put the image in a HTMLClueAligned.  */
2559                /* Man, this is *so* gross.  */
2560                HTMLClueAligned *aligned = HTML_CLUEALIGNED (html_cluealigned_new (NULL, 0, 0, clue->max_width, 100));
2561                HTML_CLUE (aligned)->halign = align;
2562                html_clue_append (HTML_CLUE (aligned), HTML_OBJECT (image));
2563                append_element (e, clue, HTML_OBJECT (aligned));
2564        }
2565
2566        /* no close tag */
2567        html_element_free (element);
2568}
2569
2570
2571static void
2572element_parse_meta (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2573{
2574        int refresh = 0;
2575        int refresh_delay = 0;
2576        gchar *refresh_url = NULL;
2577       
2578        html_string_tokenizer_tokenize( e->st, str + 5, " >" );
2579        while ( html_string_tokenizer_has_more_tokens (e->st) ) {
2580
2581                const gchar* token = html_string_tokenizer_next_token(e->st);
2582                if ( strncasecmp( token, "http-equiv=", 11 ) == 0 ) {
2583                        if ( strncasecmp( token + 11, "refresh", 7 ) == 0 )
2584                                refresh = 1;
2585                } else if ( strncasecmp( token, "content=", 8 ) == 0 ) {
2586                        if (refresh) {
2587                                const gchar *content;
2588                                content = token + 8;
2589                               
2590                                /* The time in seconds until the refresh */
2591                                refresh_delay = atoi(content);
2592                               
2593                                html_string_tokenizer_tokenize(e->st, content, ",;> ");
2594                                while ( html_string_tokenizer_has_more_tokens (e->st) ) {
2595                                        const gchar* token = html_string_tokenizer_next_token(e->st);
2596                                        if ( strncasecmp( token, "url=", 4 ) == 0 )
2597                                                refresh_url = g_strdup (token + 4);
2598                                }
2599                               
2600                                g_signal_emit (e, signals [REDIRECT], 0, refresh_url, refresh_delay);
2601                                if(refresh_url)
2602                                        g_free(refresh_url);
2603                        }
2604                }
2605        }
2606}
2607
2608static void
2609element_parse_map (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2610{
2611        pop_element (e, ID_MAP);
2612       
2613        html_string_tokenizer_tokenize (e->st, str + 3, " >");
2614        while (html_string_tokenizer_has_more_tokens (e->st)) {
2615                const char* token = html_string_tokenizer_next_token (e->st);
2616                if (strncasecmp (token, "name=", 5) == 0) {
2617                        const char *name = token + 5;
2618                       
2619                        html_engine_add_map (e, name);
2620                }
2621        }
2622        /* FIXME map nesting */
2623        push_block (e, ID_MAP, DISPLAY_NONE, block_end_map, FALSE, FALSE);
2624}
2625
2626
2627/* list parsers */
2628static HTMLListType
2629get_list_type (gchar c)
2630{
2631        switch (c) {
2632        case 'i':
2633                return HTML_LIST_TYPE_ORDERED_LOWER_ROMAN;
2634        case 'I':
2635                return HTML_LIST_TYPE_ORDERED_UPPER_ROMAN;
2636        case 'a':
2637                return HTML_LIST_TYPE_ORDERED_LOWER_ALPHA;
2638        case 'A':
2639                return HTML_LIST_TYPE_ORDERED_UPPER_ALPHA;
2640        case '1':
2641        default:
2642                return HTML_LIST_TYPE_ORDERED_ARABIC;           
2643        }
2644}
2645
2646static void
2647block_end_item (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2648{
2649        finish_flow (e, clue);
2650}
2651
2652static void
2653element_parse_li (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2654{
2655        HTMLListType listType;
2656        gint listLevel;
2657        gint itemNumber;
2658       
2659        listType = HTML_LIST_TYPE_UNORDERED;
2660        listLevel = 1;
2661        itemNumber = 1;
2662       
2663        pop_element (e, ID_LI);
2664       
2665        if (!html_stack_is_empty (e->listStack)) {
2666                HTMLList *top;
2667               
2668                top = html_stack_top (e->listStack);
2669               
2670                listType = top->type;
2671                itemNumber = top->itemNumber;
2672
2673                if (html_stack_count (e->listStack) == 1 && listType == HTML_LIST_TYPE_BLOCKQUOTE)
2674                        top->type = listType = HTML_LIST_TYPE_UNORDERED;
2675        }
2676       
2677        html_string_tokenizer_tokenize (e->st, str + 3, " >");
2678        while (html_string_tokenizer_has_more_tokens (e->st)) {
2679                const gchar *token = html_string_tokenizer_next_token (e->st);
2680               
2681                if (!strncasecmp (token, "value=", 6))
2682                        itemNumber = atoi (token + 6);
2683                else if (!strncasecmp (token, "type=", 5))
2684                        listType = get_list_type (token [5]);
2685        }
2686       
2687        if (!html_stack_is_empty (e->listStack)) {
2688                HTMLList *list;
2689               
2690                list = html_stack_top (e->listStack);
2691                list->itemNumber = itemNumber + 1;
2692        }
2693       
2694        e->flow = flow_new (e, HTML_CLUEFLOW_STYLE_LIST_ITEM, listType, itemNumber, HTML_CLEAR_NONE);
2695        html_clueflow_set_item_color (HTML_CLUEFLOW (e->flow), current_color (e));
2696       
2697        html_clue_append (HTML_CLUE (clue), e->flow);
2698        e->avoid_para = TRUE;
2699        push_block (e, ID_LI, DISPLAY_BLOCK, block_end_item, FALSE, FALSE);
2700}
2701
2702static void
2703block_end_list (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2704{
2705        html_list_destroy (html_stack_pop (e->listStack));
2706
2707        finish_flow (e, clue);
2708
2709        e->avoid_para = FALSE;
2710}
2711
2712static void
2713element_parse_ol (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2714{
2715        HTMLListType listType = HTML_LIST_TYPE_ORDERED_ARABIC;
2716       
2717        pop_element (e, ID_LI);
2718
2719        html_string_tokenizer_tokenize( e->st, str + 3, " >" );
2720       
2721        while (html_string_tokenizer_has_more_tokens (e->st)) {
2722                const char* token;
2723               
2724                token = html_string_tokenizer_next_token (e->st);
2725                if (strncasecmp( token, "type=", 5 ) == 0)
2726                        listType = get_list_type (token [5]);
2727        }
2728       
2729        html_stack_push (e->listStack, html_list_new (listType));
2730        push_block (e, ID_OL, DISPLAY_BLOCK, block_end_list, FALSE, FALSE);
2731        finish_flow (e, clue);
2732}
2733
2734static void
2735element_parse_ul (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2736{
2737        pop_element (e, ID_LI);
2738       
2739        html_string_tokenizer_tokenize (e->st, str + 3, " >");
2740        while (html_string_tokenizer_has_more_tokens (e->st))
2741                html_string_tokenizer_next_token (e->st);
2742       
2743        html_stack_push (e->listStack, html_list_new (HTML_LIST_TYPE_UNORDERED));
2744        push_block (e, ID_UL, DISPLAY_BLOCK, block_end_list, FALSE, FALSE);
2745        e->avoid_para = TRUE;
2746        finish_flow (e, clue);
2747}
2748
2749static void
2750element_parse_blockquote (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2751{
2752        gboolean type = HTML_LIST_TYPE_BLOCKQUOTE;
2753       
2754        pop_element (e, ID_LI);
2755
2756        html_string_tokenizer_tokenize (e->st, str + 11, " >");
2757        while (html_string_tokenizer_has_more_tokens (e->st)) {
2758                const char *token = html_string_tokenizer_next_token (e->st);
2759                if (strncasecmp (token, "type=", 5) == 0) {
2760                        if (strncasecmp (token + 5, "cite", 5) == 0) {
2761                                type = HTML_LIST_TYPE_BLOCKQUOTE_CITE;
2762                        }
2763                }       
2764        }
2765       
2766        html_stack_push (e->listStack, html_list_new (type));
2767        push_block (e, ID_BLOCKQUOTE, DISPLAY_BLOCK, block_end_list, FALSE, FALSE);
2768        e->avoid_para = TRUE;
2769        finish_flow (e, clue);
2770}
2771
2772static void
2773block_end_glossary (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2774{
2775        html_list_destroy (html_stack_pop (e->listStack));
2776        block_end_item (e, clue, elem);
2777}
2778
2779static void
2780element_parse_dd (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2781{
2782        pop_element (e, ID_DT);
2783        pop_element (e, ID_DD);
2784       
2785        close_flow (e, clue);
2786       
2787        push_block (e, ID_DD, DISPLAY_BLOCK, block_end_glossary, FALSE, FALSE);
2788        html_stack_push (e->listStack, html_list_new (HTML_LIST_TYPE_GLOSSARY_DD));
2789}
2790
2791static void
2792element_parse_dt (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2793{
2794        pop_element (e, ID_DT);
2795        pop_element (e, ID_DD);
2796       
2797        close_flow (e, clue);
2798       
2799        /* FIXME this should set the item flag */
2800        push_block (e, ID_DT, DISPLAY_BLOCK, block_end_item, FALSE, FALSE);             
2801}
2802
2803static void
2804element_parse_dl (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2805{
2806        close_flow (e, clue);
2807       
2808        push_block (e, ID_DL, DISPLAY_BLOCK, block_end_list, FALSE, FALSE);
2809        html_stack_push (e->listStack, html_list_new (HTML_LIST_TYPE_GLOSSARY_DL));
2810}
2811
2812static void
2813element_parse_dir (HTMLEngine *e, HTMLObject *clue, const char *str)
2814{
2815        pop_element (e, ID_LI);
2816        finish_flow (e, clue);
2817       
2818        push_block (e, ID_DIR, DISPLAY_BLOCK, block_end_list, FALSE, FALSE);
2819        html_stack_push (e->listStack, html_list_new (HTML_LIST_TYPE_DIR));
2820       
2821        /* FIXME shouldn't it create a new flow? */
2822}
2823
2824static void
2825element_parse_option (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2826{
2827        HTMLElement *element;
2828        gchar *value = NULL;
2829        gboolean selected = FALSE;
2830       
2831        if (!e->formSelect)
2832                return;
2833       
2834        element = html_element_new (e, str);
2835
2836        html_element_get_attr (element, "value", &value);
2837       
2838        if (html_element_has_attr (element, "selected"))
2839                selected = TRUE;
2840       
2841        element->style = html_style_set_display (element->style, DISPLAY_NONE);
2842
2843        pop_element (e,  ID_OPTION);
2844        html_select_add_option (e->formSelect, value, selected);
2845       
2846        e->inOption = TRUE;
2847        g_string_assign (e->formText, "");
2848       
2849        element->exitFunc = block_end_option;
2850        html_stack_push (e->span_stack, element);
2851}
2852
2853static void
2854element_parse_select (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2855{
2856        HTMLElement *element;
2857        char *value;
2858        char *name = NULL;     
2859        int size = 0;
2860        gboolean multi = FALSE;
2861       
2862        if (!e->form)
2863                return;
2864       
2865        element = html_element_new (e, str);
2866       
2867        if (html_element_get_attr (element, "name", &value))
2868                name = g_strdup (value);
2869       
2870        if (html_element_get_attr (element, "size", &value))
2871                size = atoi (value);
2872
2873        if (html_element_has_attr (element, "multiple"))
2874                multi = TRUE;
2875
2876        element->style = html_style_set_display (element->style, DISPLAY_NONE);
2877
2878        e->formSelect = HTML_SELECT (html_select_new (GTK_WIDGET(e->widget), name, size, multi));
2879        html_form_add_element (e->form, HTML_EMBEDDED ( e->formSelect ));
2880        append_element (e, clue, HTML_OBJECT (e->formSelect));
2881        g_free(name);
2882
2883        element->exitFunc = block_end_select;
2884        html_stack_push (e->span_stack, element);
2885}
2886
2887
2888/* table parsing logic */
2889static void
2890block_end_table (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2891{
2892        HTMLTable *table;
2893        HTMLHAlignType table_align = elem->miscData1;
2894        HTMLHAlignType clue_align = elem->miscData2;
2895
2896        pop_clue_style (e);
2897        table = html_stack_top (e->table_stack);
2898        html_stack_pop (e->table_stack);
2899
2900        if (table) {
2901                if (table->col == 0 && table->row == 0) {
2902                        DT(printf ("deleting empty table %p\n", table);)
2903                        html_object_destroy (HTML_OBJECT (table));
2904                        return;
2905                }
2906                 
2907                if (table_align != HTML_HALIGN_LEFT && table_align != HTML_HALIGN_RIGHT) {
2908
2909                        finish_flow (e, clue);
2910
2911                        DT(printf ("unaligned table(%p)\n", table);)
2912                        append_element (e, clue, HTML_OBJECT (table));
2913
2914                        if (table_align == HTML_HALIGN_NONE && e->flow)
2915                                /* use the alignment we saved from when the clue was created */
2916                                HTML_CLUE (e->flow)->halign = clue_align;
2917                        else {
2918                                /* for centering we don't need to create a cluealigned */
2919                                HTML_CLUE (e->flow)->halign = table_align;
2920                        }
2921
2922                        close_flow (e, clue);
2923                } else {
2924                        HTMLClueAligned *aligned = HTML_CLUEALIGNED (html_cluealigned_new (NULL, 0, 0, clue->max_width, 100));
2925                        HTML_CLUE (aligned)->halign = table_align;
2926
2927                        DT(printf ("ALIGNED table(%p)\n", table);)
2928
2929                        html_clue_append (HTML_CLUE (aligned), HTML_OBJECT (table));
2930                        append_element (e, clue, HTML_OBJECT (aligned));
2931                }
2932        }
2933}
2934
2935static void
2936block_end_inline_table (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
2937{
2938        pop_clue_style (e);
2939        html_stack_pop (e->table_stack);       
2940}
2941
2942static void
2943close_current_table (HTMLEngine *e)
2944{
2945        HTMLElement *span;
2946        GList *item;
2947       
2948        for (item = e->span_stack->list; item; item = item->next) {
2949                span = item->data;
2950               
2951                DT(printf ("%d:", span->id);)
2952                if (span->style->display == DISPLAY_TABLE)
2953                        break;
2954
2955                if (span->style->display == DISPLAY_TABLE_CELL) {
2956                        DT(printf ("found cell\n");)
2957                        return;
2958                }
2959        }
2960
2961        DT(printf ("pop_table\n");)
2962        pop_element_by_type (e, DISPLAY_TABLE);
2963}
2964
2965static void
2966element_parse_table (HTMLEngine *e, HTMLObject *clue, const gchar *str)
2967{
2968        HTMLElement *element;
2969        HTMLTable *table;
2970        char *value;
2971        HTMLLength *len;
2972
2973        gint padding = 1;
2974        gint spacing = 2;
2975        gint border = 0;
2976       
2977        /* see test16.html test0023.html and test0024.html */
2978        /* pop_element (e, ID_A); */
2979       
2980        element = html_element_new (e, str);
2981
2982        if (html_element_get_attr (element, "cellpadding", &value) && value)
2983                padding = atoi (value);
2984       
2985        if (html_element_get_attr (element, "cellspacing", &value) && value)
2986                spacing = atoi (value);
2987
2988        if (html_element_get_attr (element, "border", &value)) {
2989                if (value && *value)
2990                        border = atoi (value);
2991                else
2992                        border = 1;
2993        }
2994        if (html_element_get_attr (element, "width", &value))
2995                element->style = html_style_add_width (element->style, value);
2996       
2997        if (html_element_get_attr (element, "align", &value))
2998                element->style = html_style_add_text_align (element->style, parse_halign (value, HTML_HALIGN_NONE));
2999
3000        if (html_element_get_attr (element, "bgcolor", &value)
3001            && !e->defaultSettings->forceDefault) {
3002                GdkColor color;
3003               
3004                if (html_parse_color (value, &color)) {
3005                        HTMLColor *hcolor = html_color_new_from_gdk_color  (&color);
3006                        element->style = html_style_add_background_color (element->style, hcolor);
3007                        html_color_unref (hcolor);
3008                }
3009        }
3010       
3011        if (html_element_get_attr (element, "background", &value)
3012            && !e->defaultSettings->forceDefault)
3013                element->style = html_style_add_background_image (element->style, value);
3014
3015        element->style = html_style_set_display (element->style, DISPLAY_TABLE);
3016
3017        html_element_parse_coreattrs (element);
3018
3019        switch (element->style->display) {
3020        case DISPLAY_TABLE:
3021                close_current_table (e);
3022               
3023                len = element->style->width;
3024                table = HTML_TABLE (html_table_new (len && len->type != HTML_LENGTH_TYPE_PERCENT ? len->val : 0,
3025                                                    len && len->type == HTML_LENGTH_TYPE_PERCENT ? len->val : 0,
3026                                                    padding, spacing, border));
3027               
3028                if (element->style->bg_color)
3029                        table->bgColor = gdk_color_copy ((GdkColor *)element->style->bg_color);
3030               
3031                if (element->style->bg_image)
3032                        table->bgPixmap = html_image_factory_register (e->image_factory, NULL, element->style->bg_image, FALSE);
3033               
3034                html_stack_push (e->table_stack, table);
3035                push_clue_style (e);
3036
3037                element->miscData1 = element->style->text_align;
3038                element->miscData2 = current_alignment (e);
3039                element->exitFunc = block_end_table;
3040                html_stack_push (e->span_stack, element);
3041
3042                e->avoid_para = FALSE;
3043                break;
3044        case DISPLAY_INLINE_TABLE:
3045                close_current_table (e);
3046               
3047                len = element->style->width;
3048                table = HTML_TABLE (html_table_new (len && len->type != HTML_LENGTH_TYPE_PERCENT ? len->val : 0,
3049                                                    len && len->type == HTML_LENGTH_TYPE_PERCENT ? len->val : 0,
3050                                                    padding, spacing, border));
3051               
3052                if (element->style->bg_color)
3053                        table->bgColor = gdk_color_copy ((GdkColor *)element->style->bg_color);
3054               
3055                if (element->style->bg_image)
3056                        table->bgPixmap = html_image_factory_register (e->image_factory, NULL, element->style->bg_image, FALSE);
3057               
3058                html_stack_push (e->table_stack, table);
3059                push_clue_style (e);
3060
3061                element->exitFunc = block_end_inline_table;
3062                html_stack_push (e->span_stack, element);
3063
3064                append_element (e, clue, HTML_OBJECT (table));
3065                break;
3066        default:
3067                html_element_push (element, e, clue);
3068                break;
3069        }
3070
3071
3072}
3073     
3074static void
3075block_end_row (HTMLEngine *e, HTMLObject *clue, HTMLElement *elem)
3076{
3077        HTMLTable *table = html_stack_top (e->table_stack);
3078       
3079        if (table) {
3080                html_table_end_row (table);
3081        }
3082}
3083
3084static void
3085block_ensure_row (HTMLEngine *e)
3086{
3087        HTMLElement *span;
3088        HTMLTable *table;
3089        GList *item;
3090
3091        table = html_stack_top (e->table_stack);
3092        if (!table)
3093                return;
3094       
3095        for (item = e->span_stack->list; item; item = item->next) {
3096                span = item->data;
3097
3098                DT(printf ("%d:", span->id);)
3099                if (span->style->display == DISPLAY_TABLE_ROW) {
3100                        DT(printf ("no ensure row\n");)
3101                        return;
3102                }
3103
3104                if (span->style->display == DISPLAY_TABLE)
3105                        break;
3106                   
3107        }
3108       
3109        html_table_start_row (table);
3110        push_block_element (e, ID_TR, NULL, DISPLAY_TABLE_ROW, block_end_row, 0, 0);
3111}
3112
3113static void
3114element_parse_tr (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3115{
3116        HTMLElement *element;
3117        char *value;
3118
3119        element = html_element_new (e, str);
3120       
3121        if (html_element_get_attr (element, "valign", &value)) {
3122                if (strncasecmp (value, "top", 3) == 0)
3123                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_TOP);
3124                else if (strncasecmp (value, "bottom", 6) == 0)
3125                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_BOTTOM);
3126                else
3127                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_MIDDLE);
3128        }
3129       
3130        if (html_element_get_attr (element, "align", &value))
3131                element->style = html_style_add_text_align (element->style, parse_halign (value, HTML_HALIGN_NONE));
3132       
3133        if (html_element_get_attr (element, "bgcolor", &value)) {
3134                GdkColor color;
3135               
3136                if (html_parse_color (value, &color)) {
3137                        HTMLColor *hcolor = html_color_new_from_gdk_color (&color);
3138                        element->style = html_style_add_background_color (element->style, hcolor);
3139                        html_color_unref (hcolor);
3140                }
3141        }
3142       
3143        if (html_element_get_attr (element, "background", &value) && value && *value)
3144                element->style = html_style_add_background_image (element->style, value);
3145       
3146        element->style = html_style_set_display (element->style, DISPLAY_TABLE_ROW);
3147        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_MIDDLE);
3148
3149        html_element_parse_coreattrs  (element);
3150        html_element_push (element, e, clue);
3151}
3152
3153static void
3154element_parse_caption (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3155{
3156        HTMLTable *table = html_stack_top (e->table_stack);
3157        HTMLStyle *style = NULL;
3158        HTMLClueV *caption;
3159        HTMLVAlignType capAlign = HTML_VALIGN_MIDDLE;
3160
3161        /* CAPTIONS are all wrong they don't even get render */
3162        /* Make sure this is a valid position for a caption */
3163        if (!table)
3164                        return;
3165       
3166        pop_element_by_type (e, DISPLAY_TABLE_ROW);
3167        pop_element_by_type (e, DISPLAY_TABLE_CAPTION);
3168
3169        /*       
3170        pop_element (e, ID_TR);
3171        pop_element (e, ID_CAPTION);
3172        */
3173       
3174        html_string_tokenizer_tokenize( e->st, str + 7, " >" );
3175        while ( html_string_tokenizer_has_more_tokens (e->st) ) {
3176                const char* token = html_string_tokenizer_next_token(e->st);
3177                if ( strncasecmp( token, "align=", 6 ) == 0) {
3178                        if ( strncasecmp( token+6, "top", 3 ) == 0)
3179                                capAlign = HTML_VALIGN_TOP;
3180                }
3181        }
3182       
3183        caption = HTML_CLUEV (html_cluev_new (0, 0, 100));
3184       
3185        e->flow = 0;
3186       
3187        style = html_style_add_text_align (style, HTML_HALIGN_CENTER);
3188       
3189        push_clue (e, HTML_OBJECT (caption));
3190        push_block_element (e, ID_CAPTION, style, DISPLAY_TABLE_CAPTION, block_end_cell, 0, 0);
3191       
3192        table->caption = caption;
3193        //FIXME caption alignment should be based on the flow.... or something....
3194        table->capAlign = capAlign;             
3195}
3196
3197static void
3198element_parse_cell (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3199{
3200        HTMLTable *table = html_stack_top (e->table_stack);
3201        gint rowSpan = 1;
3202        gint colSpan = 1;
3203        HTMLTableCell *cell = NULL;
3204        char *image_url = NULL;
3205        gboolean heading;
3206        gboolean no_wrap = FALSE;
3207        HTMLElement *element;
3208        char *value;
3209        HTMLLength *len;
3210       
3211        element = html_element_new (e, str);
3212
3213        heading = !strcasecmp (g_quark_to_string (element->id), "th");
3214       
3215        element->style = html_style_unset_decoration (element->style, 0xffff);
3216        element->style = html_style_set_font_size (element->style, GTK_HTML_FONT_STYLE_SIZE_3);
3217        element->style = html_style_set_display (element->style, DISPLAY_TABLE_CELL);
3218       
3219        if (heading) {
3220                element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_BOLD);
3221                element->style = html_style_add_text_align (element->style, HTML_HALIGN_CENTER);
3222        }
3223
3224        /* begin shared with row */
3225        if (html_element_get_attr (element, "valign", &value)) {
3226                if (strncasecmp (value, "top", 3) == 0)
3227                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_TOP);
3228                else if (strncasecmp (value, "bottom", 6) == 0)
3229                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_BOTTOM);
3230                else
3231                        element->style = html_style_add_text_valign (element->style, HTML_VALIGN_MIDDLE);
3232        }
3233       
3234        if (html_element_get_attr (element, "align", &value))
3235                element->style = html_style_add_text_align (element->style, parse_halign (value, element->style->text_align));
3236       
3237        if (html_element_get_attr (element, "bgcolor", &value)) {
3238                GdkColor color;
3239               
3240                if (html_parse_color (value, &color)) {
3241                        HTMLColor *hcolor = html_color_new_from_gdk_color (&color);
3242                        element->style = html_style_add_background_color (element->style, hcolor);
3243                        html_color_unref (hcolor);
3244                }
3245        }
3246       
3247        if (html_element_get_attr (element, "background", &value) && value && *value)
3248                element->style = html_style_add_background_image (element->style, value);
3249        /* end shared with row */
3250
3251        if (html_element_get_attr (element, "rowspan", &value)) {
3252                rowSpan = atoi (value);
3253                if (rowSpan < 1)
3254                        rowSpan = 1;
3255        }
3256               
3257        if (html_element_get_attr (element, "colspan", &value)) {
3258                        colSpan = atoi (value);
3259                        if (colSpan < 1)
3260                                colSpan = 1;
3261        }
3262
3263        if (html_element_get_attr (element, "height", &value))
3264                element->style = html_style_add_height (element->style, value);
3265
3266        if (html_element_get_attr (element, "width", &value))
3267                element->style = html_style_add_width (element->style, value);
3268               
3269        if (html_element_has_attr (element, "nowrap"))
3270                        no_wrap = TRUE;
3271       
3272        html_element_parse_coreattrs (element);
3273
3274        if (!table)
3275                return;
3276       
3277        pop_element_by_type (e, DISPLAY_TABLE_CELL);
3278        pop_element_by_type (e, DISPLAY_TABLE_CAPTION);
3279
3280        cell = HTML_TABLE_CELL (html_table_cell_new (rowSpan, colSpan, table->padding));
3281        cell->no_wrap = no_wrap;
3282        cell->heading = heading;
3283
3284        html_object_set_bg_color (HTML_OBJECT (cell), element->style->bg_color ? &element->style->bg_color->color : &current_row_bg_color (e)->color);
3285
3286        image_url = element->style->bg_image ? element->style->bg_image : current_row_bg_image (e);     
3287        if (image_url) {
3288                HTMLImagePointer *ip;
3289               
3290                ip = html_image_factory_register(e->image_factory, NULL, image_url, FALSE);
3291                html_table_cell_set_bg_pixmap (cell, ip);
3292        }
3293       
3294        HTML_CLUE (cell)->valign = element->style->text_valign != HTML_VALIGN_NONE ? element->style->text_valign : current_row_valign (e);
3295        HTML_CLUE (cell)->halign = element->style->text_align != HTML_HALIGN_NONE ? element->style->text_align : current_row_align (e);
3296       
3297        len = element->style->width;
3298        if (len && len->type != HTML_LENGTH_TYPE_FRACTION)
3299                html_table_cell_set_fixed_width (cell, len->val, len->type == HTML_LENGTH_TYPE_PERCENT);
3300
3301        len = element->style->height;
3302        if (len && len->type != HTML_LENGTH_TYPE_FRACTION)
3303                html_table_cell_set_fixed_height (cell, len->val, len->type == HTML_LENGTH_TYPE_PERCENT);
3304       
3305        block_ensure_row (e);
3306        html_table_add_cell (table, cell);
3307        push_clue (e, HTML_OBJECT (cell));
3308
3309        element->exitFunc = block_end_cell;
3310        html_stack_push (e->span_stack, element);
3311}
3312
3313static void
3314element_parse_textarea (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3315{
3316        gchar *name = NULL;
3317        gint rows = 5, cols = 40;
3318       
3319        if (!e->form)
3320                return;
3321       
3322        html_string_tokenizer_tokenize (e->st, str + 9, " >");
3323        while (html_string_tokenizer_has_more_tokens (e->st)) {
3324                const gchar *token = html_string_tokenizer_next_token (e->st);
3325               
3326                if (strncasecmp (token, "name=", 5) == 0) {
3327                                name = g_strdup(token + 5);
3328                } else if (strncasecmp (token, "rows=", 5) == 0) {
3329                        rows = atoi (token + 5);
3330                } else if (strncasecmp (token, "cols=", 5) == 0) {
3331                        cols = atoi (token + 5);
3332                }
3333        }
3334       
3335        e->formTextArea = HTML_TEXTAREA (html_textarea_new (GTK_WIDGET(e->widget), name, rows, cols));
3336        html_form_add_element (e->form, HTML_EMBEDDED ( e->formTextArea ));
3337       
3338        append_element (e, clue, HTML_OBJECT (e->formTextArea));
3339       
3340        g_string_assign (e->formText, "");
3341        e->inTextArea = TRUE;
3342       
3343        g_free(name);
3344        push_block (e, ID_TEXTAREA, DISPLAY_BLOCK, block_end_textarea, 0, 0);
3345}
3346
3347/* inline elements */
3348static void
3349element_parse_big (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3350{
3351        HTMLElement *element = html_element_new (e, str);
3352
3353        element->style = html_style_set_font_size (element->style, GTK_HTML_FONT_STYLE_SIZE_4);
3354        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3355
3356        html_element_parse_coreattrs  (element);
3357        html_element_push (element, e, clue);
3358}
3359
3360static void
3361element_parse_cite (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3362{
3363        HTMLElement *element = html_element_new (e, str);
3364
3365        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_ITALIC | GTK_HTML_FONT_STYLE_BOLD);
3366        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3367
3368        html_element_parse_coreattrs  (element);
3369        html_element_push (element, e, clue);
3370
3371}
3372
3373static void
3374element_parse_small (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3375{
3376        HTMLElement *element = html_element_new (e, str);
3377
3378        element->style = html_style_set_font_size (element->style, GTK_HTML_FONT_STYLE_SIZE_2);
3379        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3380
3381        html_element_parse_coreattrs  (element);
3382        html_element_push (element, e, clue);
3383}
3384
3385static void
3386element_parse_sub (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3387{
3388        HTMLElement *element = html_element_new (e, str);
3389
3390        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_SUBSCRIPT);
3391        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3392
3393        html_element_parse_coreattrs  (element);
3394        html_element_push (element, e, clue);
3395}
3396
3397static void
3398element_parse_sup (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3399{
3400        HTMLElement *element = html_element_new (e, str);
3401
3402        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_SUPERSCRIPT);
3403        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3404
3405        html_element_parse_coreattrs  (element);
3406        html_element_push (element, e, clue);
3407}
3408
3409static void
3410element_parse_inline_strikeout (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3411{
3412        HTMLElement *element = html_element_new (e, str);
3413
3414        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_STRIKEOUT);
3415        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3416
3417        html_element_parse_coreattrs  (element);
3418        html_element_push (element, e, clue);
3419}
3420
3421static void
3422element_parse_u (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3423{
3424        HTMLElement *element = html_element_new (e, str);
3425
3426        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_UNDERLINE);
3427        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3428
3429        html_element_parse_coreattrs  (element);
3430        html_element_push (element, e, clue);
3431}
3432
3433static void
3434element_parse_inline_fixed (HTMLEngine *e, HTMLObject *clue, const char *str )
3435{
3436        HTMLElement *element = html_element_new (e, str);
3437
3438        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_FIXED);
3439        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3440
3441        html_element_parse_coreattrs  (element);
3442        html_element_push (element, e, clue);
3443}
3444
3445static void
3446element_parse_inline_italic (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3447{
3448        HTMLElement *element = html_element_new (e, str);
3449
3450        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_ITALIC);
3451        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3452
3453        html_element_parse_coreattrs  (element);
3454        html_element_push (element, e, clue);
3455}
3456
3457static void
3458element_parse_inline_bold (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3459{
3460        HTMLElement *element = html_element_new (e, str);
3461
3462        element->style = html_style_set_decoration (element->style, GTK_HTML_FONT_STYLE_BOLD);
3463        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3464
3465        html_element_parse_coreattrs  (element);
3466        html_element_push (element, e, clue);
3467}
3468
3469static void
3470element_parse_span (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3471{
3472        HTMLElement *element = html_element_new (e, str);
3473
3474        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3475
3476        html_element_parse_coreattrs  (element);
3477        html_element_push (element, e, clue);
3478}
3479
3480static void
3481element_parse_font (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3482{
3483        HTMLElement *element = html_element_new (e, str);
3484        char *value;
3485       
3486        if (html_element_get_attr (element, "size", &value)) {
3487                gint size = atoi (value);
3488               
3489                /* FIXME implement basefont */
3490                if (*value == '+' || *value == '-')
3491                        size += GTK_HTML_FONT_STYLE_SIZE_3;
3492               
3493                size = CLAMP (size, GTK_HTML_FONT_STYLE_SIZE_1, GTK_HTML_FONT_STYLE_SIZE_MAX);
3494                element->style = html_style_set_font_size (element->style, size);
3495        }
3496       
3497        if (html_element_get_attr (element, "face", &value)) {
3498                        element->style = html_style_add_font_face (element->style, value);
3499        }
3500       
3501        if (html_element_get_attr (element, "color", &value)) {
3502                GdkColor color;
3503
3504                if (html_parse_color (value, &color)) {
3505                        HTMLColor *html_color = NULL;
3506
3507                        html_color = html_color_new_from_gdk_color (&color);
3508                        element->style = html_style_add_color (element->style, html_color);
3509                        html_color_unref (html_color);
3510                }
3511        }
3512
3513        element->style = html_style_set_display (element->style, DISPLAY_INLINE);
3514
3515        html_element_parse_coreattrs  (element);
3516        html_element_push (element, e, clue);
3517}
3518
3519
3520
3521/* Parsing dispatch table.  */
3522typedef void (*HTMLParseFunc)(HTMLEngine *p, HTMLObject *clue, const gchar *str);
3523typedef struct _HTMLDispatchEntry {
3524        char *name;
3525        HTMLParseFunc func;
3526} HTMLDispatchEntry;
3527
3528HTMLDispatchEntry basic_table[] = {
3529        {ID_A,                element_parse_a},
3530        {"area",              element_parse_area},
3531        {ID_ADDRESS,          element_parse_address},
3532        {ID_B,                element_parse_inline_bold},
3533        {"base",              element_parse_base},
3534        {ID_BIG,              element_parse_big},
3535        {ID_BLOCKQUOTE,       element_parse_blockquote},
3536        {ID_BODY,             element_parse_body},
3537        {ID_CAPTION,          element_parse_caption},
3538        {ID_CENTER,           element_parse_center},
3539        {ID_CITE,             element_parse_cite},
3540        {ID_CODE,             element_parse_inline_fixed},
3541        {ID_DIR,              element_parse_dir},
3542        {ID_DIV,              element_parse_div},
3543        {"data",              element_parse_data},
3544        {ID_DL,               element_parse_dl},
3545        {ID_DT,               element_parse_dt},
3546        {ID_DD,               element_parse_dd},
3547        {ID_LI,               element_parse_li},
3548        {ID_EM,               element_parse_inline_italic},
3549        {ID_FONT,             element_parse_font},
3550        {ID_FORM,             element_parse_form},
3551        {"frameset",          element_parse_frameset},
3552        {"frame",             element_parse_frame},
3553        {ID_MAP,              element_parse_map},
3554        {"meta",              element_parse_meta},
3555        {"noframe",           element_parse_noframe},
3556        {ID_I,                element_parse_inline_italic},
3557        {"img",               element_parse_img},
3558        {"input",             element_parse_input},
3559        {"iframe",            element_parse_iframe},
3560        {ID_KBD,              element_parse_inline_fixed},
3561        {ID_OL,               element_parse_ol},
3562        {ID_OPTION,           element_parse_option},   
3563        {"object",            element_parse_object},
3564        {"param",             element_parse_param},
3565        {ID_PRE,              element_parse_pre},
3566        {ID_SMALL,            element_parse_small},
3567        {ID_SPAN,             element_parse_span},
3568        {ID_STRONG,           element_parse_inline_bold},
3569        {ID_SELECT,           element_parse_select},
3570        {ID_S,                element_parse_inline_strikeout},
3571        {ID_SUB,              element_parse_sub},
3572        {ID_SUP,              element_parse_sup},
3573        {ID_STRIKE,           element_parse_inline_strikeout},
3574        {ID_U,                element_parse_u},
3575        {ID_UL,               element_parse_ul},
3576        {ID_TEXTAREA,         element_parse_textarea},
3577        {ID_TABLE,            element_parse_table},
3578        {ID_TD,               element_parse_cell},
3579        {ID_TH,               element_parse_cell},
3580        {ID_TR,               element_parse_tr},
3581        {ID_TT,               element_parse_inline_fixed},
3582        {"title",             element_parse_title},
3583        {ID_VAR,              element_parse_inline_fixed},
3584        /*
3585         * the following elements have special behaviors for the close tags
3586         * so we dispatch on the close element as well
3587         */
3588        {"hr",                element_parse_hr},
3589        {"h1",                element_parse_heading},
3590        {"h2",                element_parse_heading},
3591        {"h3",                element_parse_heading},
3592        {"h4",                element_parse_heading},
3593        {"h5",                element_parse_heading},
3594        {"h6",                element_parse_heading},
3595        /* a /h1 after an h2 will close the h1 so we special case */
3596        {"/h1",               element_end_heading},
3597        {"/h2",               element_end_heading},
3598        {"/h3",               element_end_heading},
3599        {"/h4",               element_end_heading},
3600        {"/h5",               element_end_heading},
3601        {"/h6",               element_end_heading},
3602        /* p and br check the close marker themselves */
3603        {"p",                 element_parse_p},
3604        {"/p",                element_parse_p},
3605        {"br",                element_parse_br},
3606        {"/br",               element_parse_br},
3607        {NULL,                NULL}
3608};
3609
3610static GHashTable *
3611dispatch_table_new (HTMLDispatchEntry *entry)
3612{
3613        GHashTable *table = g_hash_table_new (g_str_hash, g_str_equal);
3614        gint i = 0;
3615
3616        while (entry[i].name) {
3617                g_hash_table_insert (table, entry[i].name, &entry[i]);
3618                i++;
3619        }
3620       
3621        return table;
3622}
3623
3624
3625static void
3626parse_one_token (HTMLEngine *e, HTMLObject *clue, const gchar *str)
3627{
3628        static GHashTable *basic = NULL;
3629        char *name;
3630        HTMLDispatchEntry *entry;
3631
3632        if (basic == NULL)
3633                basic = dispatch_table_new (basic_table);
3634
3635        if (*str == '<') {
3636                str++;
3637        } else {
3638                /* bad element */
3639                g_warning ("found token with no open");
3640                return;
3641        }
3642       
3643        name = parse_element_name (str);
3644
3645        if (!name)
3646                return;
3647
3648        entry = g_hash_table_lookup (basic, name);
3649
3650        if (entry) {
3651                /* found a custom handler use it */
3652                DT (printf ("found handler for <%s>\n", name);)
3653                (*entry->func)(e, clue, str);
3654        } else if (*name == '/') {
3655                /* generic close element */
3656                DT (printf ("generic close handler for <%s>\n", name);)
3657                pop_element (e, name + 1);
3658        } else {
3659                /* unknown open element do nothing for now */
3660                DT (printf ("generic open handler for <%s>\n", name);)
3661        }
3662
3663        g_free (name);
3664}
3665
3666
3667GType
3668html_engine_get_type (void)
3669{
3670        static GType html_engine_type = 0;
3671
3672        if (html_engine_type == 0) {
3673                static const GTypeInfo html_engine_info = {
3674                        sizeof (HTMLEngineClass),
3675                        NULL,
3676                        NULL,
3677                        (GClassInitFunc) html_engine_class_init,
3678                        NULL,
3679                        NULL,
3680                        sizeof (HTMLEngine),
3681                        1,
3682                        (GInstanceInitFunc) html_engine_init,
3683                };
3684                html_engine_type = g_type_register_static (G_TYPE_OBJECT, "HTMLEngine", &html_engine_info, 0);
3685        }
3686
3687        return html_engine_type;
3688}
3689
3690static void
3691clear_selection (HTMLEngine *e)
3692{
3693        if (e->selection) {
3694                html_interval_destroy (e->selection);
3695                e->selection = NULL;
3696        }
3697}
3698
3699static void
3700html_engine_finalize (GObject *object)
3701{
3702        HTMLEngine *engine;
3703        GList *p;
3704
3705        engine = HTML_ENGINE (object);
3706
3707        /* it is critical to destroy timers immediately so that
3708         * if widgets contained in the object tree manage to iterate the
3709         * mainloop we don't reenter in an inconsistant state.
3710         */
3711        if (engine->timerId != 0) {
3712                g_source_remove (engine->timerId);
3713                engine->timerId = 0;
3714        }
3715        if (engine->updateTimer != 0) {
3716                g_source_remove (engine->updateTimer);
3717                engine->updateTimer = 0;
3718        }
3719        if (engine->thaw_idle_id != 0) {
3720                g_source_remove (engine->thaw_idle_id);
3721                engine->thaw_idle_id = 0;
3722        }
3723        if (engine->blinking_timer_id != 0) {
3724                g_source_remove (engine->blinking_timer_id);
3725                engine->blinking_timer_id = 0;
3726        }
3727        if (engine->redraw_idle_id != 0) {
3728                g_source_remove (engine->redraw_idle_id);
3729                engine->redraw_idle_id = 0;
3730        }
3731
3732        /* remove all the timers associated with image pointers also */
3733        if (engine->image_factory) {
3734                html_image_factory_stop_animations (engine->image_factory);
3735        }
3736
3737        /* timers live in the selection updater too. */
3738        if (engine->selection_updater) {
3739                html_engine_edit_selection_updater_destroy (engine->selection_updater);
3740                engine->selection_updater = NULL;
3741        }
3742       
3743        if (engine->undo) {
3744                html_undo_destroy (engine->undo);
3745                engine->undo = NULL;
3746        }
3747        html_engine_clipboard_clear (engine);
3748
3749        if (engine->invert_gc != NULL) {
3750                g_object_unref (engine->invert_gc);
3751                engine->invert_gc = NULL;
3752        }
3753
3754        if (engine->cursor) {
3755                html_cursor_destroy (engine->cursor);
3756                engine->cursor = NULL;
3757        }
3758        if (engine->mark) {
3759                html_cursor_destroy (engine->mark);
3760                engine->mark = NULL;
3761        }
3762
3763        if (engine->ht) {
3764                html_tokenizer_destroy (engine->ht);
3765                engine->ht = NULL;
3766        }
3767
3768        if (engine->st) {
3769                html_string_tokenizer_destroy (engine->st);
3770                engine->st = NULL;
3771        }
3772
3773        if (engine->settings) {
3774                html_settings_destroy (engine->settings);
3775                engine->settings = NULL;
3776        }
3777
3778        if (engine->defaultSettings) {
3779                html_settings_destroy (engine->defaultSettings);
3780                engine->defaultSettings = NULL;
3781        }
3782
3783        if (engine->insertion_color) {
3784                html_color_unref (engine->insertion_color);
3785                engine->insertion_color = NULL;
3786        }
3787
3788        if (engine->clue != NULL) {
3789                HTMLObject *clue = engine->clue;
3790               
3791                /* extra safety in reentrant situations
3792                 * remove the clue before we destroy it
3793                 */
3794                engine->clue = engine->parser_clue = NULL;
3795                html_object_destroy (clue);
3796        }
3797
3798        if (engine->bgPixmapPtr) {
3799                html_image_factory_unregister (engine->image_factory, engine->bgPixmapPtr, NULL);
3800                engine->bgPixmapPtr = NULL;
3801        }
3802
3803        if (engine->image_factory) {
3804                html_image_factory_free (engine->image_factory);
3805                engine->image_factory = NULL;
3806        }
3807
3808        if (engine->painter) {
3809                g_object_unref (G_OBJECT (engine->painter));
3810                engine->painter = NULL;
3811        }
3812
3813        if (engine->body_stack) {
3814                while (!html_stack_is_empty (engine->body_stack))
3815                        pop_clue (engine);
3816
3817                html_stack_destroy (engine->body_stack);
3818                engine->body_stack = NULL;
3819        }
3820
3821        if (engine->span_stack) {
3822                html_stack_destroy (engine->span_stack);
3823                engine->span_stack = NULL;
3824        }
3825
3826        if (engine->clueflow_style_stack) {
3827                html_stack_destroy (engine->clueflow_style_stack);
3828                engine->clueflow_style_stack = NULL;
3829        }
3830
3831        if (engine->frame_stack) {
3832                html_stack_destroy (engine->frame_stack);
3833                engine->frame_stack = NULL;
3834        }
3835       
3836        if (engine->table_stack) {
3837                html_stack_destroy (engine->table_stack);
3838                engine->table_stack = NULL;
3839        }
3840
3841        if (engine->listStack) {
3842                html_stack_destroy (engine->listStack);
3843                engine->listStack = NULL;
3844        }
3845
3846        if (engine->embeddedStack) {
3847                html_stack_destroy (engine->embeddedStack);
3848                engine->embeddedStack = NULL;
3849        }
3850
3851        if (engine->tempStrings) {
3852                for (p = engine->tempStrings; p != NULL; p = p->next)
3853                        g_free (p->data);
3854                g_list_free (engine->tempStrings);
3855                engine->tempStrings = NULL;
3856        }
3857
3858        if (engine->draw_queue) {
3859                html_draw_queue_destroy (engine->draw_queue);
3860                engine->draw_queue = NULL;
3861        }
3862
3863        if (engine->search_info) {
3864                html_search_destroy (engine->search_info);
3865                engine->search_info = NULL;
3866        }
3867
3868        clear_selection (engine);
3869        html_engine_map_table_clear (engine);
3870        html_engine_id_table_clear (engine);
3871        html_engine_clear_all_class_data (engine);
3872
3873        if (engine->insertion_url) {
3874                g_free (engine->insertion_url);
3875                engine->insertion_url = NULL;
3876        }
3877
3878        if (engine->insertion_target) {
3879                g_free (engine->insertion_target);
3880                engine->insertion_target = NULL;
3881        }
3882
3883        G_OBJECT_CLASS (parent_class)->finalize (object);
3884}
3885
3886static void
3887html_engine_set_property (GObject *object, guint id, const GValue *value, GParamSpec *pspec)
3888{
3889        HTMLEngine *engine = HTML_ENGINE (object);
3890
3891        if (id == 1) {
3892                GtkHTMLClassProperties *prop;
3893
3894                engine->widget          = GTK_HTML (g_value_get_object (value));
3895                engine->painter         = html_gdk_painter_new (GTK_WIDGET (engine->widget), TRUE);
3896                engine->settings        = html_settings_new (GTK_WIDGET (engine->widget));
3897                engine->defaultSettings = html_settings_new (GTK_WIDGET (engine->widget));
3898
3899                engine->insertion_color = html_colorset_get_color (engine->settings->color_set, HTMLTextColor);
3900                html_color_ref (engine->insertion_color);
3901
3902                prop = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (engine->widget))->properties;
3903        }
3904}
3905
3906static void
3907html_engine_class_init (HTMLEngineClass *klass)
3908{
3909        GObjectClass *object_class;
3910        GParamSpec *pspec;
3911
3912        object_class = G_OBJECT_CLASS (klass);
3913
3914        parent_class = g_type_class_ref (G_TYPE_OBJECT);
3915
3916        signals [SET_BASE] =
3917                g_signal_new ("set_base",
3918                              G_TYPE_FROM_CLASS (object_class),
3919                              G_SIGNAL_RUN_FIRST,
3920                              G_STRUCT_OFFSET (HTMLEngineClass, set_base),
3921                              NULL, NULL,
3922                              g_cclosure_marshal_VOID__STRING,
3923                              G_TYPE_NONE, 1,
3924                              G_TYPE_STRING);
3925
3926        signals [SET_BASE_TARGET] =
3927                g_signal_new ("set_base_target",
3928                              G_TYPE_FROM_CLASS (object_class),
3929                              G_SIGNAL_RUN_FIRST,
3930                              G_STRUCT_OFFSET (HTMLEngineClass, set_base_target),
3931                              NULL, NULL,
3932                              g_cclosure_marshal_VOID__STRING,
3933                              G_TYPE_NONE, 1,
3934                              G_TYPE_STRING);
3935
3936        signals [LOAD_DONE] =
3937                g_signal_new ("load_done",
3938                              G_TYPE_FROM_CLASS (object_class),
3939                              G_SIGNAL_RUN_FIRST,
3940                              G_STRUCT_OFFSET (HTMLEngineClass, load_done),
3941                              NULL, NULL,
3942                              g_cclosure_marshal_VOID__VOID,
3943                              G_TYPE_NONE, 0);
3944
3945        signals [TITLE_CHANGED] =
3946                g_signal_new ("title_changed",
3947                              G_TYPE_FROM_CLASS (object_class),
3948                              G_SIGNAL_RUN_FIRST,
3949                              G_STRUCT_OFFSET (HTMLEngineClass, title_changed),
3950                              NULL, NULL,
3951                              g_cclosure_marshal_VOID__VOID,
3952                              G_TYPE_NONE, 0);
3953
3954        signals [URL_REQUESTED] =
3955                g_signal_new ("url_requested",
3956                              G_TYPE_FROM_CLASS (object_class),
3957                              G_SIGNAL_RUN_FIRST,
3958                              G_STRUCT_OFFSET (HTMLEngineClass, url_requested),
3959                              NULL, NULL,
3960                              html_g_cclosure_marshal_VOID__STRING_POINTER,
3961                              G_TYPE_NONE, 2,
3962                              G_TYPE_STRING,
3963                              G_TYPE_POINTER);
3964
3965        signals [DRAW_PENDING] =
3966                g_signal_new ("draw_pending",
3967                              G_TYPE_FROM_CLASS (object_class),
3968                              G_SIGNAL_RUN_FIRST,
3969                              G_STRUCT_OFFSET (HTMLEngineClass, draw_pending),
3970                              NULL, NULL,
3971                              g_cclosure_marshal_VOID__VOID,
3972                              G_TYPE_NONE, 0);
3973
3974        signals [REDIRECT] =
3975                g_signal_new ("redirect",
3976                              G_TYPE_FROM_CLASS (object_class),
3977                              G_SIGNAL_RUN_FIRST,
3978                              G_STRUCT_OFFSET (HTMLEngineClass, redirect),
3979                              NULL, NULL,
3980                              html_g_cclosure_marshal_VOID__POINTER_INT,
3981                              G_TYPE_NONE, 2,
3982                              G_TYPE_STRING,
3983                              G_TYPE_INT);
3984
3985        signals [SUBMIT] =
3986                g_signal_new ("submit",
3987                              G_TYPE_FROM_CLASS (object_class),
3988                              G_SIGNAL_RUN_FIRST,
3989                              G_STRUCT_OFFSET (HTMLEngineClass, submit),
3990                              NULL, NULL,
3991                              html_g_cclosure_marshal_VOID__STRING_STRING_STRING,
3992                              G_TYPE_NONE, 3,
3993                              G_TYPE_STRING,
3994                              G_TYPE_STRING,
3995                              G_TYPE_STRING);
3996
3997        signals [OBJECT_REQUESTED] =
3998                g_signal_new ("object_requested",
3999                              G_TYPE_FROM_CLASS (object_class),
4000                              G_SIGNAL_RUN_LAST,
4001                              G_STRUCT_OFFSET (HTMLEngineClass, object_requested),
4002                              NULL, NULL,
4003                              html_g_cclosure_marshal_BOOL__OBJECT,
4004                              G_TYPE_BOOLEAN, 1,
4005                              G_TYPE_OBJECT);
4006
4007        object_class->finalize = html_engine_finalize;
4008        object_class->set_property = html_engine_set_property;
4009
4010        pspec = g_param_spec_object ("html", NULL, NULL, GTK_TYPE_HTML, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
4011        g_object_class_install_property (object_class, 1, pspec);
4012
4013        html_engine_init_magic_links ();
4014
4015        /* Initialize the HTML objects.  */
4016        html_types_init ();
4017}
4018
4019static void
4020html_engine_init (HTMLEngine *engine)
4021{
4022        engine->clue = engine->parser_clue = NULL;
4023
4024        /* STUFF might be missing here!   */
4025        engine->freeze_count = 0;
4026        engine->thaw_idle_id = 0;
4027        engine->pending_expose = NULL;
4028
4029        engine->window = NULL;
4030        engine->invert_gc = NULL;
4031
4032        /* settings, colors and painter init */
4033
4034        engine->newPage = FALSE;
4035        engine->allow_frameset = FALSE;
4036
4037        engine->editable = FALSE;
4038        engine->caret_mode = FALSE;
4039        engine->clipboard = NULL;
4040        engine->clipboard_stack = NULL;
4041        engine->selection_stack  = NULL;
4042
4043        engine->ht = html_tokenizer_new ();
4044        engine->st = html_string_tokenizer_new ();
4045        engine->image_factory = html_image_factory_new(engine);
4046
4047        engine->undo = html_undo_new ();
4048
4049        engine->body_stack = html_stack_new (NULL);
4050        engine->span_stack = html_stack_new (free_element);
4051        engine->clueflow_style_stack = html_stack_new (NULL);
4052        engine->frame_stack = html_stack_new (NULL);
4053        engine->table_stack = html_stack_new (NULL);
4054
4055        engine->listStack = html_stack_new ((HTMLStackFreeFunc) html_list_destroy);
4056        engine->embeddedStack = html_stack_new (g_object_unref);
4057
4058        engine->url = NULL;
4059        engine->target = NULL;
4060
4061        engine->leftBorder = LEFT_BORDER;
4062        engine->rightBorder = RIGHT_BORDER;
4063        engine->topBorder = TOP_BORDER;
4064        engine->bottomBorder = BOTTOM_BORDER;
4065
4066        engine->inPre = FALSE;
4067        engine->inTitle = FALSE;
4068
4069        engine->tempStrings = NULL;
4070
4071        engine->draw_queue = html_draw_queue_new (engine);
4072
4073        engine->map = NULL;
4074        engine->formList = NULL;
4075
4076        engine->avoid_para = FALSE;
4077
4078        engine->have_focus = FALSE;
4079
4080        engine->cursor = html_cursor_new ();
4081        engine->mark = NULL;
4082        engine->cursor_hide_count = 1;
4083
4084        engine->timerId = 0;
4085        engine->updateTimer = 0;
4086
4087        engine->blinking_timer_id = 0;
4088        engine->blinking_status = FALSE;
4089
4090        engine->insertion_font_style = GTK_HTML_FONT_STYLE_DEFAULT;
4091        engine->insertion_url = NULL;
4092        engine->insertion_target = NULL;
4093        engine->selection = NULL;
4094        engine->shift_selection = FALSE;
4095        engine->selection_mode = FALSE;
4096        engine->block_selection = 0;
4097        engine->cursor_position_stack = NULL;
4098
4099        engine->selection_updater = html_engine_edit_selection_updater_new (engine);
4100
4101        engine->search_info = NULL;
4102        engine->need_spell_check = FALSE;
4103
4104        html_engine_print_set_min_split_index (engine, .75);
4105
4106        engine->block = FALSE;
4107        engine->block_images = FALSE;
4108        engine->save_data = FALSE;
4109        engine->saved_step_count = -1;
4110
4111        engine->map_table = NULL;
4112
4113        engine->expose = FALSE;
4114        engine->need_update = FALSE;
4115
4116        engine->language = NULL;
4117
4118}
4119
4120HTMLEngine *
4121html_engine_new (GtkWidget *w)
4122{
4123        HTMLEngine *engine;
4124
4125        engine = g_object_new (HTML_TYPE_ENGINE, "html", w, NULL);
4126
4127        return engine;
4128}
4129
4130void
4131html_engine_realize (HTMLEngine *e,
4132                     GdkWindow *window)
4133{
4134        GdkGCValues gc_values;
4135
4136        g_return_if_fail (e != NULL);
4137        g_return_if_fail (window != NULL);
4138        g_return_if_fail (e->window == NULL);
4139
4140        e->window = window;
4141
4142        html_gdk_painter_realize (HTML_GDK_PAINTER (e->painter), window);
4143
4144        gc_values.function = GDK_INVERT;
4145        e->invert_gc = gdk_gc_new_with_values (e->window, &gc_values, GDK_GC_FUNCTION);
4146
4147        if (e->need_update)
4148                html_engine_schedule_update (e);
4149}
4150
4151void
4152html_engine_unrealize (HTMLEngine *e)
4153{
4154        if (e->thaw_idle_id != 0) {
4155                g_source_remove (e->thaw_idle_id);
4156                e->thaw_idle_id = 0;
4157        }
4158
4159        html_gdk_painter_unrealize (HTML_GDK_PAINTER (e->painter));
4160
4161        e->window = NULL;
4162}
4163
4164
4165/* This function makes sure @engine can be edited properly.  In order
4166   to be editable, the beginning of the document must have the
4167   following structure:
4168   
4169     HTMLClueV (cluev)
4170       HTMLClueFlow (head)
4171         HTMLObject (child) */
4172void
4173html_engine_ensure_editable (HTMLEngine *engine)
4174{
4175        HTMLObject *cluev;
4176        HTMLObject *head;
4177        HTMLObject *child;
4178        g_return_if_fail (engine != NULL);
4179        g_return_if_fail (HTML_IS_ENGINE (engine));
4180
4181        cluev = engine->clue;
4182        if (cluev == NULL)
4183                engine->clue = engine->parser_clue = cluev = html_cluev_new (0, 0, 100);
4184
4185        head = HTML_CLUE (cluev)->head;
4186        if (head == NULL || HTML_OBJECT_TYPE (head) != HTML_TYPE_CLUEFLOW) {
4187                HTMLObject *clueflow;
4188
4189                clueflow = flow_new (engine, HTML_CLUEFLOW_STYLE_NORMAL, HTML_LIST_TYPE_BLOCKQUOTE, 0, HTML_CLEAR_NONE);
4190                html_clue_prepend (HTML_CLUE (cluev), clueflow);
4191
4192                head = clueflow;
4193        }
4194
4195        child = HTML_CLUE (head)->head;
4196        if (child == NULL) {
4197                HTMLObject *text;
4198
4199                text = text_new (engine, "", engine->insertion_font_style, engine->insertion_color);
4200                html_text_set_font_face (HTML_TEXT (text), current_font_face (engine));
4201                html_clue_prepend (HTML_CLUE (head), text);
4202        }
4203}
4204
4205
4206void
4207html_engine_draw_background (HTMLEngine *e,
4208                             gint x, gint y, gint w, gint h)
4209{
4210        HTMLImagePointer *bgpixmap;
4211        GdkPixbuf *pixbuf = NULL;
4212
4213        /* return if no background pixmap is set */
4214        bgpixmap = e->bgPixmapPtr;
4215        if (bgpixmap && bgpixmap->animation) {
4216                pixbuf = gdk_pixbuf_animation_get_static_image (bgpixmap->animation);
4217        }
4218
4219        html_painter_draw_background (e->painter,
4220                                      &html_colorset_get_color_allocated (e->settings->color_set,
4221                                                                          e->painter, HTMLBgColor)->color,
4222                                      pixbuf, x, y, w, h, x, y);
4223}
4224
4225void
4226html_engine_stop_parser (HTMLEngine *e)
4227{
4228        if (!e->parsing)
4229                return;
4230        html_engine_flush (e);
4231       
4232        e->parsing = FALSE;
4233
4234        pop_element_by_type (e, DISPLAY_DOCUMENT);
4235
4236        html_stack_clear (e->span_stack);
4237        html_stack_clear (e->clueflow_style_stack);
4238        html_stack_clear (e->frame_stack);
4239        html_stack_clear (e->table_stack);
4240
4241        html_stack_clear (e->listStack);
4242}
4243
4244/* used for cleaning up the id hash table */
4245static gboolean
4246id_table_free_func (gpointer key, gpointer val, gpointer data)
4247{
4248        g_free (key);
4249        return TRUE;
4250}
4251
4252static void
4253html_engine_id_table_clear (HTMLEngine *e)
4254{
4255        if (e->id_table) {
4256                g_hash_table_foreach_remove (e->id_table, id_table_free_func, NULL);
4257                g_hash_table_destroy (e->id_table);
4258                e->id_table = NULL;
4259        }
4260}
4261
4262static gboolean
4263class_data_free_func (gpointer key, gpointer val, gpointer data)
4264{
4265        g_free (key);
4266        g_free (val);
4267
4268        return TRUE;
4269}
4270
4271static gboolean
4272class_data_table_free_func (gpointer key, gpointer val, gpointer data)
4273{
4274        GHashTable *t;
4275
4276        t = (GHashTable *) val;
4277        g_hash_table_foreach_remove (t, class_data_free_func, NULL);
4278        g_hash_table_destroy (t);
4279
4280        g_free (key);
4281
4282        return TRUE;
4283}
4284
4285static void
4286html_engine_class_data_clear (HTMLEngine *e)
4287{
4288        if (e->class_data) {
4289                g_hash_table_foreach_remove (e->class_data, class_data_table_free_func, NULL);
4290                g_hash_table_destroy (e->class_data);
4291                e->class_data = NULL;
4292        }
4293}
4294
4295/* #define LOG_INPUT */
4296
4297GtkHTMLStream *
4298html_engine_begin (HTMLEngine *e, char *content_type)
4299{
4300        GtkHTMLStream *new_stream;
4301
4302        html_engine_clear_all_class_data (e);
4303        html_tokenizer_begin (e->ht, content_type);
4304       
4305        html_engine_stop_parser (e);
4306        e->writing = TRUE;
4307        e->begin = TRUE;
4308        html_engine_set_focus_object (e, NULL, 0);
4309
4310        html_engine_id_table_clear (e);
4311        html_engine_class_data_clear (e);
4312        html_engine_map_table_clear (e);
4313        html_image_factory_stop_animations (e->image_factory);
4314
4315        new_stream = gtk_html_stream_new (GTK_HTML (e->widget),
4316                                          html_engine_stream_types,
4317                                          html_engine_stream_write,
4318                                          html_engine_stream_end,
4319                                          e);
4320#ifdef LOG_INPUT
4321        new_stream = gtk_html_stream_log_new (GTK_HTML (e->widget), new_stream);
4322#endif
4323        html_engine_opened_streams_set (e, 1);
4324        e->stopped = FALSE;
4325       
4326        e->newPage = TRUE;
4327        clear_selection (e);
4328
4329        html_engine_thaw_idle_flush (e);
4330
4331        g_slist_free (e->cursor_position_stack);
4332        e->cursor_position_stack = NULL;
4333
4334        push_block_element (e, ID_DOCUMENT, NULL, DISPLAY_DOCUMENT, NULL, 0, 0);
4335
4336        return new_stream;
4337}
4338
4339static void
4340html_engine_stop_forall (HTMLObject *o, HTMLEngine *e, gpointer data)
4341{
4342        if (HTML_IS_FRAME (o))
4343                GTK_HTML (HTML_FRAME (o)->html)->engine->stopped = TRUE;
4344        else if (HTML_IS_IFRAME (o))
4345                GTK_HTML (HTML_IFRAME (o)->html)->engine->stopped = TRUE;
4346}
4347
4348void
4349html_engine_stop (HTMLEngine *e)
4350{
4351        e->stopped = TRUE;
4352        html_object_forall (e->clue, e, html_engine_stop_forall, NULL);
4353}
4354
4355char *engine_content_types[]= {"text/html", NULL};
4356
4357static char **
4358html_engine_stream_types (GtkHTMLStream *handle,
4359                          gpointer data)
4360{
4361        return engine_content_types;
4362}
4363
4364static void
4365html_engine_stream_write (GtkHTMLStream *handle,
4366                          const gchar *buffer,
4367                          size_t size,
4368                          gpointer data)
4369{
4370        HTMLEngine *e;
4371
4372        e = HTML_ENGINE (data);
4373
4374        if (buffer == NULL)
4375                return;
4376
4377        html_tokenizer_write (e->ht, buffer, size == -1 ? strlen (buffer) : size);
4378
4379        if (e->parsing && e->timerId == 0) {
4380                e->timerId = gtk_timeout_add (10, (GtkFunction) html_engine_timer_event, e);
4381        }
4382}
4383
4384static void
4385update_embedded (GtkWidget *widget, gpointer data)
4386{
4387        HTMLObject *obj;
4388
4389        /* FIXME: this is a hack to update all the embedded widgets when
4390         * they get moved off screen it isn't gracefull, but it should be a effective
4391         * it also duplicates the draw_obj function in the drawqueue function very closely
4392         * the common code in these functions should be merged and removed, but until then
4393         * enjoy having your objects out of the way :)
4394         */
4395       
4396        obj = HTML_OBJECT (g_object_get_data (G_OBJECT (widget), "embeddedelement"));
4397        if (obj && html_object_is_embedded (obj)) {
4398                HTMLEmbedded *emb = HTML_EMBEDDED (obj);
4399               
4400                if (emb->widget) {
4401                        gint x, y;
4402
4403                        html_object_engine_translation (obj, NULL, &x, &y);
4404                       
4405                        x += obj->x;
4406                        y += obj->y - obj->ascent;
4407
4408                        if (!emb->widget->parent) {
4409                                gtk_layout_put (GTK_LAYOUT (emb->parent), emb->widget, x, y);
4410                        } else {
4411                                gtk_layout_move (GTK_LAYOUT(emb->parent), emb->widget, x, y);
4412                        }
4413                }
4414        }
4415}
4416
4417static gboolean
4418html_engine_update_event (HTMLEngine *e)
4419{
4420        DI (printf ("html_engine_update_event idle %p\n", e);)
4421
4422        e->updateTimer = 0;
4423
4424        if (html_engine_get_editable (e))
4425                html_engine_hide_cursor (e);
4426        html_engine_calc_size (e, FALSE);
4427
4428        if (GTK_LAYOUT (e->widget)->vadjustment == NULL
4429            || ! html_gdk_painter_realized (HTML_GDK_PAINTER (e->painter))) {
4430                e->need_update = TRUE;
4431                return FALSE;
4432        }
4433
4434        e->need_update = FALSE;
4435        DI (printf ("continue %p\n", e);)
4436
4437        /* Adjust the scrollbars */
4438        if (!e->keep_scroll)
4439                gtk_html_private_calc_scrollbars (e->widget, NULL, NULL);
4440
4441        /* Scroll page to the top on first display */
4442        if (e->newPage) {
4443                gtk_adjustment_set_value (GTK_LAYOUT (e->widget)->vadjustment, 0);
4444                e->newPage = FALSE;
4445                if (! e->parsing && e->editable)
4446                        html_cursor_home (e->cursor, e);
4447        }
4448
4449        if (!e->keep_scroll) {
4450                /* Is y_offset too big? */
4451                if (html_engine_get_doc_height (e) - e->y_offset < e->height) {
4452                        e->y_offset = html_engine_get_doc_height (e) - e->height;
4453                        if (e->y_offset < 0)
4454                                e->y_offset = 0;
4455                }
4456               
4457                /* Is x_offset too big? */
4458                if (html_engine_get_doc_width (e) - e->x_offset < e->width) {
4459                        e->x_offset = html_engine_get_doc_width (e) - e->width;
4460                        if (e->x_offset < 0)
4461                                e->x_offset = 0;
4462                }
4463       
4464                gtk_adjustment_set_value (GTK_LAYOUT (e->widget)->vadjustment, e->y_offset);
4465                gtk_adjustment_set_value (GTK_LAYOUT (e->widget)->hadjustment, e->x_offset);
4466        }
4467        html_image_factory_deactivate_animations (e->image_factory);
4468        gtk_container_forall (GTK_CONTAINER (e->widget), update_embedded, e->widget);
4469        html_engine_queue_redraw_all (e);
4470
4471        if (html_engine_get_editable (e))
4472                html_engine_show_cursor (e);
4473       
4474        return FALSE;
4475}
4476
4477
4478void
4479html_engine_schedule_update (HTMLEngine *e)
4480{
4481        DI (printf ("html_engine_schedule_update (may block %d)\n", e->opened_streams));
4482        if (e->block && e->opened_streams)
4483                return;
4484        DI (printf ("html_engine_schedule_update - timer %d\n", e->updateTimer));
4485        if (e->updateTimer == 0)
4486                e->updateTimer = gtk_idle_add ((GtkFunction) html_engine_update_event, e);
4487}
4488
4489
4490gboolean
4491html_engine_goto_anchor (HTMLEngine *e,
4492                         const gchar *anchor)
4493{
4494        GtkAdjustment *vadj;
4495        HTMLAnchor *a;
4496        gint x, y;
4497
4498        g_return_val_if_fail (anchor != NULL, FALSE);
4499
4500        if (!e->clue)
4501                return FALSE;
4502
4503        x = y = 0;
4504        a = html_object_find_anchor (e->clue, anchor, &x, &y);
4505
4506        if (a == NULL) {
4507                /* g_warning ("Anchor: \"%s\" not found", anchor); */
4508                return FALSE;
4509        }
4510
4511        vadj = GTK_LAYOUT (e->widget)->vadjustment;
4512
4513        if (y < vadj->upper - vadj->page_size)
4514                gtk_adjustment_set_value (vadj, y);
4515        else
4516                gtk_adjustment_set_value (vadj, vadj->upper - vadj->page_size);
4517
4518        return TRUE;
4519}
4520
4521#if 0
4522struct {
4523        HTMLEngine *e;
4524        HTMLObject *o;
4525} respon
4526
4527static void
4528find_engine (HTMLObject *o, HTMLEngine *e, HTMLEngine **parent_engine)
4529{
4530       
4531}
4532
4533gchar *
4534html_engine_get_object_base (HTMLEngine *e, HTMLObject *o)
4535{
4536        HTMLEngine *parent_engine = NULL;
4537       
4538        g_return_if_fail (e != NULL);
4539        g_return_if_fail (o != NULL);
4540       
4541        html_object_forall (o, e, find_engine, &parent_engine);
4542
4543       
4544}
4545#endif
4546
4547static gboolean
4548html_engine_timer_event (HTMLEngine *e)
4549{
4550        static const gchar *end[] = { NULL };
4551        gint lastHeight;
4552        gboolean retval = TRUE;
4553
4554        DI (printf ("html_engine_timer_event idle %p\n", e);)
4555
4556        /* Has more tokens? */
4557        if (!html_tokenizer_has_more_tokens (e->ht) && e->writing) {
4558                retval = FALSE;
4559                goto out;
4560        }
4561
4562        /* Getting height */
4563        lastHeight = html_engine_get_doc_height (e);
4564
4565        e->parseCount = e->granularity;
4566
4567        /* Parsing body height */
4568        new_parse_body (e, end);
4569
4570        e->begin = FALSE;
4571        html_engine_schedule_update (e);
4572
4573        if (!e->parsing)
4574                retval = FALSE;
4575
4576 out:
4577        if (!retval) {
4578                if(e->updateTimer != 0) {
4579                        g_source_remove (e->updateTimer);
4580                        html_engine_update_event (e);
4581                }
4582               
4583                e->timerId = 0;
4584        }
4585
4586        return retval;
4587}
4588
4589/* This makes sure that the last HTMLClueFlow is non-empty.  */
4590static void
4591fix_last_clueflow (HTMLEngine *engine)
4592{
4593        HTMLClue *clue;
4594        HTMLClue *last_clueflow;
4595
4596        clue = HTML_CLUE (engine->clue);
4597        if (clue == NULL)
4598                return;
4599
4600        last_clueflow = HTML_CLUE (clue->tail);
4601        if (last_clueflow == NULL)
4602                return;
4603
4604        if (last_clueflow->tail != NULL)
4605                return;
4606
4607        html_clue_remove (HTML_CLUE (clue), HTML_OBJECT (last_clueflow));
4608        engine->flow = NULL;
4609}
4610
4611static void
4612html_engine_stream_end (GtkHTMLStream *stream,
4613                        GtkHTMLStreamStatus status,
4614                        gpointer data)
4615{
4616        HTMLEngine *e;
4617
4618        e = HTML_ENGINE (data);
4619
4620        e->writing = FALSE;
4621
4622        html_tokenizer_end (e->ht);
4623
4624        if (e->timerId != 0) {
4625                g_source_remove (e->timerId);
4626                e->timerId = 0;
4627        }
4628
4629        while (html_engine_timer_event (e))
4630                ;
4631
4632        if (e->opened_streams)
4633                html_engine_opened_streams_decrement (e);
4634        DI (printf ("ENGINE(%p) opened streams: %d\n", e, e->opened_streams));
4635        if (e->block && e->opened_streams == 0)
4636                html_engine_schedule_update (e);
4637
4638        fix_last_clueflow (e);
4639        html_engine_class_data_clear (e);
4640       
4641        if (e->editable) {
4642                html_engine_ensure_editable (e);
4643                html_cursor_home (e->cursor, e);
4644                e->newPage = FALSE;
4645        }
4646
4647        gtk_widget_queue_resize (GTK_WIDGET (e->widget));
4648
4649        g_signal_emit (e, signals [LOAD_DONE], 0);
4650}
4651
4652static void
4653html_engine_draw_real (HTMLEngine *e, gint x, gint y, gint width, gint height, gboolean expose)
4654{
4655        gint x1, x2, y1, y2;
4656       
4657        if (e->block && e->opened_streams)
4658                return;
4659
4660        /* printf ("html_engine_draw_real\n"); */
4661
4662        /* This case happens when the widget has not been shown yet.  */
4663        if (width == 0 || height == 0)
4664                return;
4665
4666        /* don't draw in case we are longer than available space and scrollbar is going to be shown */
4667        if (e->clue && e->clue->ascent + e->clue->descent > e->height - (html_engine_get_top_border (e) + html_engine_get_bottom_border (e))) {
4668                if (GTK_WIDGET (e->widget)->parent) {
4669                        if (GTK_IS_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)) {
4670                                if (GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar
4671                                    && !GTK_WIDGET_VISIBLE (GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar)
4672                                    && GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
4673                                        return;
4674                        }
4675                }
4676        }
4677
4678        /* don't draw in case we are shorter than available space and scrollbar is going to be hidden */
4679        if (e->clue && e->clue->ascent + e->clue->descent <= e->height - (html_engine_get_top_border (e) + html_engine_get_bottom_border (e))) {
4680                if (GTK_WIDGET (e->widget)->parent) {
4681                        if (GTK_IS_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)) {
4682                                if (GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar
4683                                    && GTK_WIDGET_VISIBLE (GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar)
4684                                    && GTK_SCROLLED_WINDOW (GTK_WIDGET (e->widget)->parent)->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
4685                                        return;
4686                        }
4687                }
4688        }
4689
4690        /* printf ("html_engine_draw_real THRU\n"); */
4691
4692        /* printf ("html_engine_draw_real %d x %d, %d\n",
4693           e->width, e->height, e->clue ? e->clue->ascent + e->clue->descent : 0); */
4694
4695        e->expose = expose;
4696       
4697        x1 = x;
4698        x2 = x + width;
4699        y1 = y;
4700        y2 = y + height;
4701       
4702        if (!html_engine_intersection (e, &x1, &y1, &x2, &y2))
4703                return;
4704       
4705        html_painter_begin (e->painter, x1, y1, x2, y2);
4706       
4707        html_engine_draw_background (e, x1, y1, x2 - x1, y2 - y1);
4708       
4709        if (e->clue) {
4710                e->clue->x = html_engine_get_left_border (e);
4711                e->clue->y = html_engine_get_top_border (e) + e->clue->ascent;
4712                html_object_draw (e->clue, e->painter, x1, y1, x2 - x1, y2 - y1, 0, 0);
4713        }
4714        html_painter_end (e->painter);
4715       
4716        if (e->editable || e->caret_mode)
4717                html_engine_draw_cursor_in_area (e, x1, y1, x2 - x1, y2 - y1);
4718
4719        e->expose = FALSE;
4720}
4721
4722void
4723html_engine_expose (HTMLEngine *e, GdkEventExpose *event)
4724{
4725        if (html_engine_frozen (e))
4726                html_engine_add_expose (e, event->area.x, event->area.y, event->area.width, event->area.height, TRUE);
4727        else
4728                html_engine_draw_real (e, event->area.x, event->area.y, event->area.width, event->area.height, TRUE);
4729}
4730
4731void
4732html_engine_draw (HTMLEngine *e, gint x, gint y, gint width, gint height)
4733{
4734        if (html_engine_frozen (e))
4735                html_engine_add_expose (e, x, y, width, height, FALSE);
4736        else
4737                html_engine_draw_real (e, x, y, width, height, FALSE);
4738}
4739
4740static gint
4741redraw_idle (HTMLEngine *e)
4742{
4743       e->redraw_idle_id = 0;
4744       e->need_redraw = FALSE;
4745       html_engine_queue_redraw_all (e);
4746
4747       return FALSE;
4748}
4749
4750void
4751html_engine_schedule_redraw (HTMLEngine *e)
4752{
4753        /* printf ("html_engine_schedule_redraw\n"); */
4754
4755        if (e->block_redraw)
4756                e->need_redraw = TRUE;
4757        else if (e->redraw_idle_id == 0) {
4758                clear_pending_expose (e);
4759                html_draw_queue_clear (e->draw_queue);
4760                e->redraw_idle_id = gtk_idle_add ((GtkFunction) redraw_idle, e);
4761        }
4762}
4763
4764void
4765html_engine_block_redraw (HTMLEngine *e)
4766{
4767        e->block_redraw ++;
4768        if (e->redraw_idle_id) {
4769                g_source_remove (e->redraw_idle_id);
4770                e->redraw_idle_id = 0;
4771                e->need_redraw = TRUE;
4772        }
4773}
4774
4775
4776void
4777html_engine_unblock_redraw (HTMLEngine *e)
4778{
4779        g_assert (e->block_redraw > 0);
4780
4781        e->block_redraw --;
4782        if (!e->block_redraw && e->need_redraw) {
4783                if (e->redraw_idle_id) {
4784                        g_source_remove (e->redraw_idle_id);
4785                        e->redraw_idle_id = 0;
4786                }
4787                redraw_idle (e);
4788        }
4789}
4790
4791
4792gint
4793html_engine_get_doc_width (HTMLEngine *e)
4794{
4795        return (e->clue ? e->clue->width : 0) + html_engine_get_left_border (e) + html_engine_get_right_border (e);
4796}
4797
4798gint
4799html_engine_get_doc_height (HTMLEngine *e)
4800{
4801        gint height;
4802
4803        if (e->clue) {
4804                height = e->clue->ascent;
4805                height += e->clue->descent;
4806                height += html_engine_get_top_border (e);
4807                height += html_engine_get_bottom_border (e);
4808
4809                return height;
4810        }
4811       
4812        return 0;
4813}
4814
4815gint
4816html_engine_calc_min_width (HTMLEngine *e)
4817{
4818        return html_object_calc_min_width (e->clue, e->painter)
4819                + html_painter_get_pixel_size (e->painter) * (html_engine_get_left_border (e) + html_engine_get_right_border (e));
4820}
4821
4822gint
4823html_engine_get_max_width (HTMLEngine *e)
4824{
4825        gint max_width;
4826
4827        if (e->widget->iframe_parent)
4828                max_width = e->widget->frame->max_width
4829                        - (html_engine_get_left_border (e) + html_engine_get_right_border (e)) * html_painter_get_pixel_size (e->painter);
4830        else
4831                max_width = html_painter_get_page_width (e->painter, e)
4832                        - (html_engine_get_left_border (e) + html_engine_get_right_border (e)) * html_painter_get_pixel_size (e->painter);
4833
4834        return MAX (0, max_width);
4835}
4836
4837gint
4838html_engine_get_max_height (HTMLEngine *e)
4839{
4840        gint max_height;
4841
4842        if (e->widget->iframe_parent)
4843                max_height = HTML_FRAME (e->widget->frame)->height
4844                        - (html_engine_get_top_border (e) + html_engine_get_bottom_border (e)) * html_painter_get_pixel_size (e->painter);
4845        else
4846                max_height = html_painter_get_page_height (e->painter, e)
4847                        - (html_engine_get_top_border (e) + html_engine_get_bottom_border (e)) * html_painter_get_pixel_size (e->painter);
4848
4849        return MAX (0, max_height);
4850}
4851
4852gboolean
4853html_engine_calc_size (HTMLEngine *e, GList **changed_objs)
4854{
4855        gint max_width; /* , max_height; */
4856        gboolean redraw_whole;
4857
4858        if (e->clue == 0)
4859                return FALSE;
4860
4861        html_object_reset (e->clue);
4862
4863        max_width = MIN (html_engine_get_max_width (e),
4864                         html_painter_get_pixel_size (e->painter)
4865                         * (MAX_WIDGET_WIDTH - html_engine_get_left_border (e) - html_engine_get_right_border (e)));
4866        /* max_height = MIN (html_engine_get_max_height (e),
4867                         html_painter_get_pixel_size (e->painter)
4868                         * (MAX_WIDGET_WIDTH - e->topBorder - e->bottomBorder)); */
4869
4870        redraw_whole = max_width != e->clue->max_width;
4871        html_object_set_max_width (e->clue, e->painter, max_width);
4872        /* html_object_set_max_height (e->clue, e->painter, max_height); */
4873        /* printf ("calc size %d\n", e->clue->max_width); */
4874        if (changed_objs)
4875                *changed_objs = NULL;
4876        html_object_calc_size (e->clue, e->painter, redraw_whole ? NULL : changed_objs);
4877
4878        e->clue->x = html_engine_get_left_border (e);
4879        e->clue->y = e->clue->ascent + html_engine_get_top_border (e);
4880
4881        return redraw_whole;
4882}
4883
4884static void
4885destroy_form (gpointer data, gpointer user_data)
4886{
4887        html_form_destroy (HTML_FORM(data));
4888}
4889
4890void
4891html_engine_parse (HTMLEngine *e)
4892{
4893        html_engine_stop_parser (e);
4894
4895        /* reset search & replace */
4896        if (e->search_info) {
4897                html_search_destroy (e->search_info);
4898                e->search_info = NULL;
4899        }
4900        if (e->replace_info) {
4901                html_replace_destroy (e->replace_info);
4902                e->replace_info = NULL;
4903        }
4904
4905        if (e->clue != NULL)
4906                html_object_destroy (e->clue);
4907
4908        g_list_foreach (e->formList, destroy_form, NULL);
4909
4910        g_list_free (e->formList);
4911
4912        e->map = NULL;
4913        e->formList = NULL;
4914        e->form = NULL;
4915        e->formSelect = NULL;
4916        e->formTextArea = NULL;
4917        e->inOption = FALSE;
4918        e->inTextArea = FALSE;
4919        e->formText = g_string_new ("");
4920
4921        e->flow = NULL;
4922
4923        /* reset to default border size */
4924        e->leftBorder   = LEFT_BORDER;
4925        e->rightBorder  = RIGHT_BORDER;
4926        e->topBorder    = TOP_BORDER;
4927        e->bottomBorder = BOTTOM_BORDER;
4928
4929        /* reset settings to default ones */
4930        html_colorset_set_by (e->settings->color_set, e->defaultSettings->color_set);
4931
4932        e->clue = e->parser_clue = html_cluev_new (html_engine_get_left_border (e), html_engine_get_top_border (e), 100);
4933        HTML_CLUE (e->clue)->valign = HTML_VALIGN_TOP;
4934        HTML_CLUE (e->clue)->halign = HTML_HALIGN_LEFT;
4935
4936        e->cursor->object = e->clue;
4937
4938        /* Free the background pixmap */
4939        if (e->bgPixmapPtr) {
4940                html_image_factory_unregister(e->image_factory, e->bgPixmapPtr, NULL);
4941                e->bgPixmapPtr = NULL;
4942        }
4943
4944        e->parsing = TRUE;
4945        e->avoid_para = FALSE;
4946
4947        e->timerId = gtk_idle_add ((GtkFunction) html_engine_timer_event, e);
4948}
4949
4950
4951HTMLObject *
4952html_engine_get_object_at (HTMLEngine *e,
4953                           gint x, gint y,
4954                           guint *offset_return,
4955                           gboolean for_cursor)
4956{
4957        HTMLObject *clue;
4958        HTMLObject *obj;
4959
4960        clue = HTML_OBJECT (e->clue);
4961        if (clue == NULL)
4962                return NULL;
4963
4964        if (for_cursor) {
4965                gint width, height;
4966
4967                width = clue->width;
4968                height = clue->ascent + clue->descent;
4969
4970                if (width == 0 || height == 0)
4971                        return NULL;
4972
4973                if (x < html_engine_get_left_border (e))
4974                        x = html_engine_get_left_border (e);
4975                else if (x >= html_engine_get_left_border (e) + width)
4976                        x = html_engine_get_left_border (e) + width - 1;
4977
4978                if (y < html_engine_get_top_border (e)) {
4979                        x = html_engine_get_left_border (e);
4980                        y = html_engine_get_top_border (e);
4981                } else if (y >= html_engine_get_top_border (e) + height) {
4982                        x = html_engine_get_left_border (e) + width - 1;
4983                        y = html_engine_get_top_border (e) + height - 1;
4984                }
4985        }
4986
4987        obj = html_object_check_point (clue, e->painter, x, y, offset_return, for_cursor);
4988
4989        return obj;
4990}
4991
4992HTMLPoint *
4993html_engine_get_point_at (HTMLEngine *e,
4994                          gint x, gint y,
4995                          gboolean for_cursor)
4996{
4997        HTMLObject *o;
4998        guint off;
4999
5000        o = html_engine_get_object_at (e, x, y, &off, for_cursor);
5001
5002        return o ? html_point_new (o, off) : NULL;
5003}
5004
5005const gchar *
5006html_engine_get_link_at (HTMLEngine *e, gint x, gint y)
5007{
5008        HTMLObject *obj;
5009        gint offset;
5010
5011        if (e->clue == NULL)
5012                return NULL;
5013
5014        obj = html_engine_get_object_at (e, x, y, &offset, FALSE);
5015
5016        if (obj != NULL)
5017                return html_object_get_url (obj, offset);
5018
5019        return NULL;
5020}
5021
5022
5023/**
5024 * html_engine_set_editable:
5025 * @e: An HTMLEngine object
5026 * @editable: A flag specifying whether the object must be editable
5027 * or not
5028 *
5029 * Make @e editable or not, according to the value of @editable.
5030 **/
5031void
5032html_engine_set_editable (HTMLEngine *e,
5033                          gboolean editable)
5034{
5035        g_return_if_fail (e != NULL);
5036        g_return_if_fail (HTML_IS_ENGINE (e));
5037
5038        if ((e->editable && editable) || (! e->editable && ! editable))
5039                return;
5040
5041        if (editable)
5042                html_engine_spell_check (e);
5043        html_engine_disable_selection (e);
5044
5045        html_engine_queue_redraw_all (e);
5046
5047        e->editable = editable;
5048
5049        if (editable) {
5050                html_engine_ensure_editable (e);
5051                html_cursor_home (e->cursor, e);
5052                e->newPage = FALSE;
5053                if (e->have_focus)
5054                        html_engine_setup_blinking_cursor (e);
5055        } else {
5056                if (e->have_focus) {
5057                        if (e->caret_mode)
5058                                html_engine_setup_blinking_cursor (e);
5059                        else
5060                                html_engine_stop_blinking_cursor (e);
5061                }
5062        }
5063
5064        gtk_html_drag_dest_set (e->widget);
5065}
5066
5067gboolean
5068html_engine_get_editable (HTMLEngine *e)
5069{
5070        g_return_val_if_fail (e != NULL, FALSE);
5071        g_return_val_if_fail (HTML_IS_ENGINE (e), FALSE);
5072
5073        if (e->editable && ! e->parsing && e->timerId == 0)
5074                return TRUE;
5075        else
5076                return FALSE;
5077}
5078
5079static void
5080set_focus (HTMLObject *o, HTMLEngine *e, gpointer data)
5081{
5082        if (HTML_IS_IFRAME (o) || HTML_IS_FRAME (o)) {
5083                HTMLEngine *cur_e = GTK_HTML (HTML_IS_FRAME (o) ? HTML_FRAME (o)->html : HTML_IFRAME (o)->html)->engine;
5084                html_painter_set_focus (cur_e->painter, GPOINTER_TO_INT (data));
5085        }
5086}
5087
5088void
5089html_engine_set_focus (HTMLEngine *engine,
5090                       gboolean have_focus)
5091{
5092        g_return_if_fail (engine != NULL);
5093        g_return_if_fail (HTML_IS_ENGINE (engine));
5094
5095        if (engine->editable || engine->caret_mode) {
5096                if (! engine->have_focus && have_focus)
5097                        html_engine_setup_blinking_cursor (engine);
5098                else if (engine->have_focus && ! have_focus)
5099                        html_engine_stop_blinking_cursor (engine);
5100        }
5101
5102        engine->have_focus = have_focus;
5103
5104        html_painter_set_focus (engine->painter, engine->have_focus);
5105        if (engine->clue)
5106                html_object_forall (engine->clue, engine, set_focus, GINT_TO_POINTER (have_focus));
5107        html_engine_redraw_selection (engine);
5108}
5109
5110
5111/*
5112  FIXME: It might be nice if we didn't allow the tokenizer to be
5113  changed once tokenizing has begin.
5114*/
5115void
5116html_engine_set_tokenizer (HTMLEngine *engine,
5117                           HTMLTokenizer *tok)
5118{
5119        g_return_if_fail (engine && HTML_IS_ENGINE (engine));
5120        g_return_if_fail (tok && HTML_IS_TOKENIZER (tok));
5121
5122        g_object_ref (G_OBJECT (tok));
5123        g_object_unref (G_OBJECT (engine->ht));
5124        engine->ht = tok;
5125}
5126
5127
5128gboolean
5129html_engine_make_cursor_visible (HTMLEngine *e)
5130{
5131        gint x1, y1, x2, y2, xo, yo;
5132
5133        g_return_val_if_fail (e != NULL, FALSE);
5134
5135        if (! e->editable && !e->caret_mode)
5136                return FALSE;
5137
5138        if (e->cursor->object == NULL)
5139                return FALSE;
5140
5141        html_object_get_cursor (e->cursor->object, e->painter, e->cursor->offset, &x1, &y1, &x2, &y2);
5142
5143        xo = e->x_offset;
5144        yo = e->y_offset;
5145
5146        if (x1 < e->x_offset)
5147                e->x_offset = x1 - html_engine_get_left_border (e);
5148        if (x1 > e->x_offset + e->width - html_engine_get_right_border (e))
5149                e->x_offset = x1 - e->width + html_engine_get_right_border (e);
5150
5151        if (y1 < e->y_offset)
5152                e->y_offset = y1 - html_engine_get_top_border (e);
5153        if (y2 >= e->y_offset + e->height - html_engine_get_bottom_border (e))
5154                e->y_offset = y2 - e->height + html_engine_get_bottom_border (e) + 1;
5155
5156        return xo != e->x_offset || yo != e->y_offset;
5157}
5158
5159
5160/* Draw queue handling.  */
5161
5162void
5163html_engine_flush_draw_queue (HTMLEngine *e)
5164{
5165        g_return_if_fail (e != NULL);
5166        g_return_if_fail (HTML_IS_ENGINE (e));
5167
5168        if (!html_engine_frozen (e)) {
5169                html_draw_queue_flush (e->draw_queue);
5170        }
5171}
5172
5173void
5174html_engine_queue_draw (HTMLEngine *e, HTMLObject *o)
5175{
5176        g_return_if_fail (e != NULL);
5177        g_return_if_fail (HTML_IS_ENGINE (e));
5178        g_return_if_fail (o != NULL);
5179
5180        html_draw_queue_add (e->draw_queue, o);
5181        /* printf ("html_draw_queue_add %p\n", o); */
5182}
5183
5184void
5185html_engine_queue_clear (HTMLEngine *e,
5186                         gint x, gint y,
5187                         guint width, guint height)
5188{
5189        g_return_if_fail (e != NULL);
5190
5191        /* if (e->freeze_count == 0) */
5192        html_draw_queue_add_clear (e->draw_queue, x, y, width, height,
5193                                   &html_colorset_get_color_allocated (e->settings->color_set,
5194                                                                       e->painter, HTMLBgColor)->color);
5195}
5196
5197
5198void
5199html_engine_form_submitted (HTMLEngine *e,
5200                            const gchar *method,
5201                            const gchar *action,
5202                            const gchar *encoding)
5203{
5204        g_signal_emit (e, signals [SUBMIT], 0, method, action, encoding);
5205}
5206
5207
5208/* Retrieving the selection as a string.  */
5209
5210gchar *
5211html_engine_get_selection_string (HTMLEngine *engine)
5212{
5213        GString *buffer;
5214        gchar *string;
5215
5216        g_return_val_if_fail (engine != NULL, NULL);
5217        g_return_val_if_fail (HTML_IS_ENGINE (engine), NULL);
5218
5219        if (engine->clue == NULL)
5220                return NULL;
5221
5222        buffer = g_string_new (NULL);
5223        html_object_append_selection_string (engine->clue, buffer);
5224
5225        string = buffer->str;
5226        g_string_free (buffer, FALSE);
5227
5228        return string;
5229}
5230
5231
5232/* Cursor normalization.  */
5233
5234void
5235html_engine_normalize_cursor (HTMLEngine *engine)
5236{
5237        g_return_if_fail (engine != NULL);
5238        g_return_if_fail (HTML_IS_ENGINE (engine));
5239
5240        html_cursor_normalize (engine->cursor);
5241        html_engine_edit_selection_updater_update_now (engine->selection_updater);
5242}
5243
5244
5245/* Freeze/thaw.  */
5246
5247gboolean
5248html_engine_frozen (HTMLEngine *engine)
5249{
5250        g_return_val_if_fail (engine != NULL, FALSE);
5251        g_return_val_if_fail (HTML_IS_ENGINE (engine), FALSE);
5252
5253        return engine->freeze_count > 0;
5254}
5255
5256void
5257html_engine_freeze (HTMLEngine *engine)
5258{
5259        g_return_if_fail (engine != NULL);
5260        g_return_if_fail (HTML_IS_ENGINE (engine));
5261
5262        if (engine->freeze_count == 0) {
5263                gtk_html_im_reset (engine->widget);
5264                html_engine_flush_draw_queue (engine);
5265                if ((HTML_IS_GDK_PAINTER (engine->painter) || HTML_IS_PLAIN_PAINTER (engine->painter)) && HTML_GDK_PAINTER (engine->painter)->window)
5266                gdk_window_process_updates (HTML_GDK_PAINTER (engine->painter)->window, FALSE);
5267        }
5268
5269
5270        html_engine_flush_draw_queue (engine);
5271        DF (printf ("html_engine_freeze %d\n", engine->freeze_count); fflush (stdout));
5272
5273        html_engine_hide_cursor (engine);
5274        engine->freeze_count++;
5275}
5276
5277static void
5278html_engine_get_viewport (HTMLEngine *e, GdkRectangle *viewport)
5279{
5280        viewport->x = e->x_offset;
5281        viewport->y = e->y_offset;
5282        viewport->width = e->width;
5283        viewport->height = e->height;
5284}
5285
5286gboolean
5287html_engine_intersection (HTMLEngine *e, gint *x1, gint *y1, gint *x2, gint *y2)
5288{
5289        HTMLEngine *top = html_engine_get_top_html_engine (e);
5290        GdkRectangle draw;
5291        GdkRectangle clip;
5292        GdkRectangle paint;
5293
5294        html_engine_get_viewport (e, &clip);
5295
5296        draw.x = *x1;
5297        draw.y = *y1;
5298        draw.width = *x2 - *x1;
5299        draw.height = *y2 - *y1;
5300
5301        if (!gdk_rectangle_intersect (&clip, &draw, &paint))
5302                return FALSE;
5303
5304        if (e != top) {
5305                GdkRectangle top_clip;
5306                gint abs_x = 0, abs_y = 0;
5307                               
5308                html_object_calc_abs_position (e->clue->parent, &abs_x, &abs_y);
5309                abs_y -= e->clue->parent->ascent;
5310
5311                html_engine_get_viewport (top, &top_clip);
5312                top_clip.x -= abs_x;
5313                top_clip.y -= abs_y;
5314
5315                if (!gdk_rectangle_intersect (&paint, &top_clip, &paint))
5316                        return FALSE;
5317        }
5318
5319
5320        *x1 = paint.x;
5321        *x2 = paint.x + paint.width;
5322        *y1 = paint.y;
5323        *y2 = paint.y + paint.height;
5324
5325        return TRUE;
5326}
5327
5328static void
5329get_changed_objects (HTMLEngine *e, GdkRegion *region, GList *changed_objs)
5330{
5331        GList *cur;
5332
5333        /* printf ("draw_changed_objects BEGIN\n"); */
5334
5335        for (cur = changed_objs; cur; cur = cur->next) {
5336                if (cur->data) {
5337                        HTMLObject *o;
5338
5339                        o = HTML_OBJECT (cur->data);
5340                        html_engine_queue_draw (e, o);
5341                } else {
5342                        cur = cur->next;
5343                        if (e->window) {
5344                                HTMLObjectClearRectangle *cr = (HTMLObjectClearRectangle *)cur->data;
5345                                HTMLObject *o;
5346                                GdkRectangle paint;
5347                                gint tx, ty;
5348                                       
5349                                o = cr->object;
5350                               
5351                                html_object_engine_translation (cr->object, e, &tx, &ty);
5352                               
5353                                paint.x = o->x + cr->x + tx;
5354                                paint.y = o->y - o->ascent + cr->y + ty;
5355                                paint.width = cr->width;
5356                                paint.height = cr->height;
5357                               
5358                                gdk_region_union_with_rect (region, &paint);
5359                        }
5360                        g_free (cur->data);
5361                }
5362        }
5363        /* printf ("draw_changed_objects END\n"); */
5364}
5365
5366
5367struct HTMLEngineExpose {
5368        GdkRectangle area;
5369        gboolean expose;
5370};
5371
5372static void
5373get_pending_expose (HTMLEngine *e, GdkRegion *region)
5374{
5375        GSList *l, *next;
5376
5377        g_assert (!html_engine_frozen (e));
5378        /* printf ("do_pending_expose\n"); */
5379
5380        for (l = e->pending_expose; l; l = next) {
5381                struct HTMLEngineExpose *r;
5382
5383                next = l->next;
5384                r = (struct HTMLEngineExpose *) l->data;
5385
5386                gdk_region_union_with_rect (region, &r->area);
5387                g_free (r);
5388        }
5389}
5390
5391static void
5392free_expose_data (gpointer data, gpointer user_data)
5393{
5394        g_free (data);
5395}
5396
5397static void
5398clear_pending_expose (HTMLEngine *e)
5399{
5400        g_slist_foreach (e->pending_expose, free_expose_data, NULL);
5401        g_slist_free (e->pending_expose);
5402        e->pending_expose = NULL;
5403}
5404
5405#ifdef CHECK_CURSOR
5406static void
5407check_cursor (HTMLEngine *e)
5408{
5409        HTMLCursor *cursor;
5410        gboolean need_spell_check;
5411
5412        cursor = html_cursor_dup (e->cursor);
5413       
5414        need_spell_check = e->need_spell_check;
5415        e->need_spell_check = FALSE;
5416
5417        while (html_cursor_backward (cursor, e))
5418                ;
5419
5420        if (cursor->position != 0) {
5421                g_warning ("check cursor failed (%d)\n", cursor->position);
5422                gnome_ok_dialog ("Eeek, BAD cursor position!\n"
5423                                 "\n"
5424                                 "If you know how to get editor to this state,\n"
5425                                 "please mail to gtkhtml-maintainers@ximian.com\n"
5426                                 "detailed description\n"
5427                                 "\n"
5428                                 "Thank you");
5429                e->cursor->position -= cursor->position;
5430        }
5431
5432        e->need_spell_check = need_spell_check;
5433        html_cursor_destroy (cursor);
5434}
5435#endif
5436
5437static gint
5438thaw_idle (gpointer data)
5439{
5440        HTMLEngine *e = HTML_ENGINE (data);
5441        GList *changed_objs;
5442        gboolean redraw_whole;
5443        gint w, h;
5444
5445        DF (printf ("thaw_idle %d\n", e->freeze_count); fflush (stdout));
5446
5447#ifdef CHECK_CURSOR
5448        check_cursor (e);
5449#endif
5450
5451        e->thaw_idle_id = 0;
5452        if (e->freeze_count != 1) {
5453                /* we have been frozen again meanwhile */
5454                DF (printf ("frozen again meanwhile\n"); fflush (stdout);)
5455                html_engine_show_cursor (e);
5456                e->freeze_count--;
5457                return FALSE;
5458        }
5459
5460        w = html_engine_get_doc_width (e) - html_engine_get_right_border (e);
5461        h = html_engine_get_doc_height (e) - html_engine_get_bottom_border (e);
5462
5463        redraw_whole = html_engine_calc_size (e, &changed_objs);
5464
5465        gtk_html_private_calc_scrollbars (e->widget, NULL, NULL);
5466        gtk_html_edit_make_cursor_visible (e->widget);
5467
5468        e->freeze_count--;
5469
5470        if (redraw_whole) {
5471                html_engine_queue_redraw_all (e);
5472        } else if (GTK_WIDGET_REALIZED (e->widget)) {
5473                gint nw, nh;
5474                GdkRegion *region = gdk_region_new ();
5475                GdkRectangle paint;
5476               
5477                get_pending_expose (e, region);
5478                get_changed_objects (e, region, changed_objs);
5479
5480                nw = html_engine_get_doc_width (e) - html_engine_get_right_border (e);
5481                nh = html_engine_get_doc_height (e) - html_engine_get_bottom_border (e);
5482
5483                if (nh < h && nh - e->y_offset < e->height) {
5484                        paint.x = e->x_offset;
5485                        paint.y = nh;
5486                        paint.width = e->width;
5487                        paint.height = e->height + e->y_offset - nh;
5488
5489                        gdk_region_union_with_rect (region, &paint);
5490                }
5491                if (nw < w && nw - e->x_offset < e->width) {
5492                        paint.x = nw;
5493                        paint.y = e->y_offset;
5494                        paint.width = e->width + e->x_offset - nw;
5495
5496                        gdk_region_union_with_rect (region, &paint);
5497                }
5498                g_list_free (changed_objs);
5499                gdk_window_invalidate_region (HTML_GDK_PAINTER (e->painter)->window, region, FALSE);
5500                gdk_region_destroy (region);
5501                html_engine_flush_draw_queue (e);
5502        }
5503        g_slist_free (e->pending_expose);
5504        e->pending_expose = NULL;
5505
5506        html_engine_show_cursor (e);
5507
5508        return FALSE;
5509}
5510
5511void
5512html_engine_thaw (HTMLEngine *engine)
5513{
5514        g_return_if_fail (engine != NULL);
5515        g_return_if_fail (HTML_IS_ENGINE (engine));
5516        g_return_if_fail (engine->freeze_count > 0);
5517
5518        if (engine->freeze_count == 1) {
5519                if (engine->thaw_idle_id == 0) {
5520                        DF (printf ("queueing thaw_idle %d\n", engine->freeze_count);)
5521                        engine->thaw_idle_id = gtk_idle_add (thaw_idle, engine);
5522                }
5523        } else {
5524                engine->freeze_count--;
5525                html_engine_show_cursor (engine);
5526        }
5527
5528        DF (printf ("html_engine_thaw %d\n", engine->freeze_count);)
5529}
5530
5531void
5532html_engine_thaw_idle_flush (HTMLEngine *e)
5533{
5534        DF (printf ("html_engine_thaw_idle_flush\n");fflush (stdout);)
5535
5536        if (e->thaw_idle_id) {
5537                g_source_remove (e->thaw_idle_id);
5538                thaw_idle (e);
5539        }
5540}
5541
5542
5543/**
5544 * html_engine_load_empty:
5545 * @engine: An HTMLEngine object
5546 *
5547 * Load an empty document into the engine.
5548 **/
5549void
5550html_engine_load_empty (HTMLEngine *engine)
5551{
5552        g_return_if_fail (engine != NULL);
5553        g_return_if_fail (HTML_IS_ENGINE (engine));
5554
5555        /* FIXME: "slightly" hackish.  */
5556        html_engine_stop_parser (engine);
5557        html_engine_parse (engine);
5558        html_engine_stop_parser (engine);
5559
5560        html_engine_ensure_editable (engine);
5561}
5562
5563void
5564html_engine_replace (HTMLEngine *e, const gchar *text, const gchar *rep_text,
5565                     gboolean case_sensitive, gboolean forward, gboolean regular,
5566                     void (*ask)(HTMLEngine *, gpointer), gpointer ask_data)
5567{
5568        if (e->replace_info)
5569                html_replace_destroy (e->replace_info);
5570        e->replace_info = html_replace_new (rep_text, ask, ask_data);
5571
5572        if (html_engine_search (e, text, case_sensitive, forward, regular))
5573                ask (e, ask_data);
5574}
5575
5576static void
5577replace (HTMLEngine *e)
5578{
5579        HTMLObject *first = HTML_OBJECT (e->search_info->found->data);
5580
5581        html_engine_edit_selection_updater_update_now (e->selection_updater);
5582
5583        if (e->replace_info->text && *e->replace_info->text) {
5584                HTMLObject *new_text;
5585
5586                new_text = text_new (e, e->replace_info->text,
5587                                     HTML_TEXT (first)->font_style,
5588                                     HTML_TEXT (first)->color);
5589                html_text_set_font_face (HTML_TEXT (new_text), HTML_TEXT (first)->face);
5590                html_engine_paste_object (e, new_text, html_object_get_length (HTML_OBJECT (new_text)));
5591        } else {
5592                html_engine_delete (e);
5593        }
5594
5595        /* update search info to point just behind replaced text */
5596        g_list_free (e->search_info->found);
5597        e->search_info->found = g_list_append (NULL, e->cursor->object);
5598        e->search_info->start_pos = e->search_info->stop_pos = e->cursor->offset - 1;
5599        e->search_info->found_bytes = 0;
5600        html_search_pop  (e->search_info);
5601        html_search_push (e->search_info, e->cursor->object->parent);
5602}
5603
5604gboolean
5605html_engine_replace_do (HTMLEngine *e, HTMLReplaceQueryAnswer answer)
5606{
5607        gboolean finished;
5608
5609        g_assert (e->replace_info);
5610
5611        switch (answer) {
5612        case RQA_ReplaceAll:
5613                html_undo_level_begin (e->undo, "Replace all", "Revert replace all");
5614                replace (e);
5615                while (html_engine_search_next (e))
5616                        replace (e);
5617                html_undo_level_end (e->undo);
5618        case RQA_Cancel:
5619                html_replace_destroy (e->replace_info);
5620                e->replace_info = NULL;
5621                html_engine_disable_selection (e);
5622                finished = TRUE;
5623                break;
5624
5625        case RQA_Replace:
5626                html_undo_level_begin (e->undo, "Replace", "Revert replace");
5627                replace (e);
5628                html_undo_level_end (e->undo);
5629        case RQA_Next:
5630                finished = !html_engine_search_next (e);
5631                if (finished)
5632                        html_engine_disable_selection (e);
5633                break;
5634        }
5635
5636        return finished;
5637}
5638
5639/* spell checking */
5640
5641static void
5642check_paragraph (HTMLObject *o, HTMLEngine *unused, HTMLEngine *e)
5643{
5644        if (HTML_OBJECT_TYPE (o) == HTML_TYPE_CLUEFLOW)
5645                html_clueflow_spell_check (HTML_CLUEFLOW (o), e, NULL);
5646}
5647
5648void
5649html_engine_spell_check (HTMLEngine *e)
5650{
5651        g_assert (HTML_IS_ENGINE (e));
5652        g_assert (e->clue);
5653
5654        e->need_spell_check = FALSE;
5655
5656        if (e->widget->editor_api && e->widget->editor_api->check_word)
5657                html_object_forall (e->clue, NULL, (HTMLObjectForallFunc) check_paragraph, e);
5658}
5659
5660static void
5661clear_spell_check (HTMLObject *o, HTMLEngine *unused, HTMLEngine *e)
5662{
5663        if (html_object_is_text (o))
5664                html_text_spell_errors_clear (HTML_TEXT (o));
5665}
5666
5667void
5668html_engine_clear_spell_check (HTMLEngine *e)
5669{
5670        g_assert (HTML_IS_ENGINE (e));
5671        g_assert (e->clue);
5672
5673        e->need_spell_check = FALSE;
5674
5675        html_object_forall (e->clue, NULL, (HTMLObjectForallFunc) clear_spell_check, e);
5676        html_engine_draw (e, e->x_offset, e->y_offset, e->width, e->height);
5677}
5678
5679gchar *
5680html_engine_get_spell_word (HTMLEngine *e)
5681{
5682        GString *text;
5683        HTMLCursor *cursor;
5684        gchar *word;
5685        gint pos;
5686        gunichar uc;
5687        gboolean cited, cited_tmp, cited2;
5688
5689        cited = FALSE;
5690        if (!html_selection_spell_word (html_cursor_get_current_char (e->cursor), &cited) && !cited
5691            && !html_selection_spell_word (html_cursor_get_prev_char (e->cursor), &cited) && !cited)
5692                return NULL;
5693
5694        cursor = html_cursor_dup (e->cursor);
5695        pos    = cursor->position;
5696        text   = g_string_new (NULL);
5697
5698        /* move to the beginning of word */
5699        cited = cited_tmp = FALSE;
5700        while (html_selection_spell_word (html_cursor_get_prev_char (cursor), &cited_tmp) || cited_tmp) {
5701                html_cursor_backward (cursor, e);
5702                if (cited_tmp)
5703                        cited_tmp = TRUE;
5704                cited_tmp = FALSE;
5705        }
5706
5707        /* move to the end of word */
5708        cited2 = FALSE;
5709        while (html_selection_spell_word (uc = html_cursor_get_current_char (cursor), &cited2) || (!cited && cited2)) {
5710                gchar out [7];
5711                gint size;
5712
5713                size = g_unichar_to_utf8 (uc, out);
5714                g_assert (size < 7);
5715                out [size] = 0;
5716                text = g_string_append (text, out);
5717                html_cursor_forward (cursor, e);
5718                cited2 = FALSE;
5719        }
5720
5721        word = text->str;
5722        g_string_free (text, FALSE);
5723        html_cursor_destroy (cursor);
5724
5725        return word;
5726}
5727
5728gboolean
5729html_engine_spell_word_is_valid (HTMLEngine *e)
5730{
5731        HTMLObject *obj;
5732        HTMLText   *text;
5733        GList *cur;
5734        gboolean valid = TRUE;
5735        gint offset;
5736        gchar prev, curr;
5737        gboolean cited;
5738
5739        cited = FALSE;
5740        prev = html_cursor_get_prev_char (e->cursor);
5741        curr = html_cursor_get_current_char (e->cursor);
5742
5743        /* if we are not in word always return TRUE so we care only about invalid words */
5744        if (!html_selection_spell_word (prev, &cited) && !cited && !html_selection_spell_word (curr, &cited) && !cited)
5745                return TRUE;
5746
5747        if (html_selection_spell_word (curr, &cited)) {
5748                gboolean end;
5749
5750                end    = (e->cursor->offset == html_object_get_length (e->cursor->object));
5751                obj    = (end) ? html_object_next_not_slave (e->cursor->object) : e->cursor->object;
5752                offset = (end) ? 0 : e->cursor->offset;
5753        } else {
5754                obj    = (e->cursor->offset) ? e->cursor->object : html_object_prev_not_slave (e->cursor->object);
5755                offset = (e->cursor->offset) ? e->cursor->offset - 1 : html_object_get_length (obj) - 1;
5756        }
5757
5758        g_assert (html_object_is_text (obj));
5759        text = HTML_TEXT (obj);
5760
5761        /* now we have text, so let search for spell_error area in it */
5762        cur = text->spell_errors;
5763        while (cur) {
5764                SpellError *se = (SpellError *) cur->data;
5765                if (se->off <= offset && offset <= se->off + se->len) {
5766                        valid = FALSE;
5767                        break;
5768                }
5769                if (offset < se->off)
5770                        break;
5771                cur = cur->next;
5772        }
5773
5774        /* printf ("is_valid: %d\n", valid); */
5775
5776        return valid;
5777}
5778
5779void
5780html_engine_replace_spell_word_with (HTMLEngine *e, const gchar *word)
5781{
5782        HTMLObject *replace = NULL;
5783        HTMLText   *orig;
5784
5785        html_engine_select_spell_word_editable (e);
5786
5787        orig = HTML_TEXT (e->mark->object);
5788        switch (HTML_OBJECT_TYPE (e->mark->object)) {
5789        case HTML_TYPE_TEXT:
5790                replace = text_new (e, word, orig->font_style, orig->color);
5791                break;
5792                /* FIXME-link case HTML_TYPE_LINKTEXT:
5793                replace = html_link_text_new (word, orig->font_style, orig->color,
5794                                              HTML_LINK_TEXT (orig)->url,
5795                                              HTML_LINK_TEXT (orig)->target);
5796                                              break; */
5797        default:
5798                g_assert_not_reached ();
5799        }
5800        html_text_set_font_face (HTML_TEXT (replace), HTML_TEXT (orig)->face);
5801        html_engine_edit_selection_updater_update_now (e->selection_updater);
5802        html_engine_paste_object (e, replace, html_object_get_length (replace));
5803}
5804
5805HTMLCursor *
5806html_engine_get_cursor (HTMLEngine *e)
5807{
5808        HTMLCursor *cursor;
5809
5810        cursor = html_cursor_new ();
5811        cursor->object = html_engine_get_object_at (e, e->widget->selection_x1, e->widget->selection_y1,
5812                                                    &cursor->offset, FALSE);
5813        return cursor;
5814}
5815
5816void
5817html_engine_set_painter (HTMLEngine *e, HTMLPainter *painter)
5818{
5819        g_return_if_fail (painter != NULL);
5820        g_return_if_fail (e != NULL);
5821
5822        g_object_ref (G_OBJECT (painter));
5823        g_object_unref (G_OBJECT (e->painter));
5824        e->painter = painter;
5825       
5826        html_object_set_painter (e->clue, painter);
5827        html_object_change_set_down (e->clue, HTML_CHANGE_ALL);
5828        html_object_reset (e->clue);
5829        html_engine_calc_size (e, FALSE);
5830}
5831
5832gint
5833html_engine_get_view_width (HTMLEngine *e)
5834{
5835        return MAX (0, (e->widget->iframe_parent
5836                ? html_engine_get_view_width (GTK_HTML (e->widget->iframe_parent)->engine)
5837                : GTK_WIDGET (e->widget)->allocation.width) - (html_engine_get_left_border (e) + html_engine_get_right_border (e)));
5838}
5839
5840gint
5841html_engine_get_view_height (HTMLEngine *e)
5842{
5843        return MAX (0, (e->widget->iframe_parent
5844                ? html_engine_get_view_height (GTK_HTML (e->widget->iframe_parent)->engine)
5845                : GTK_WIDGET (e->widget)->allocation.height) - (html_engine_get_top_border (e) + html_engine_get_bottom_border (e)));
5846}
5847
5848/* beginnings of ID support */
5849void
5850html_engine_add_object_with_id (HTMLEngine *e, const gchar *id, HTMLObject *obj)
5851{
5852        gpointer old_key;
5853        gpointer old_val;
5854
5855        if (e->id_table == NULL)
5856                e->id_table = g_hash_table_new (g_str_hash, g_str_equal);
5857
5858        if (!g_hash_table_lookup_extended (e->id_table, id, &old_key, &old_val))
5859                old_key = NULL;
5860
5861        g_hash_table_insert (e->id_table, old_key ? old_key : g_strdup (id), obj);
5862}
5863
5864HTMLObject *
5865html_engine_get_object_by_id (HTMLEngine *e, const gchar *id)
5866{
5867        if (e->id_table == NULL)
5868                return NULL;
5869
5870        return (HTMLObject *) g_hash_table_lookup (e->id_table, id);
5871}
5872
5873GHashTable *
5874html_engine_get_class_table (HTMLEngine *e, const gchar *class_name)
5875{
5876        return (class_name && e->class_data) ? g_hash_table_lookup (e->class_data, class_name) : NULL;
5877}
5878
5879static inline GHashTable *
5880get_class_table_sure (HTMLEngine *e, const gchar *class_name)
5881{
5882        GHashTable *t;
5883
5884        if (e->class_data == NULL)
5885                e->class_data = g_hash_table_new (g_str_hash, g_str_equal);
5886
5887        t = html_engine_get_class_table (e, class_name);
5888        if (!t) {
5889                t = g_hash_table_new (g_str_hash, g_str_equal);
5890                g_hash_table_insert (e->class_data, g_strdup (class_name), t);
5891        }
5892
5893        return t;
5894}
5895
5896void
5897html_engine_set_class_data (HTMLEngine *e, const gchar *class_name, const gchar *key, const gchar *value)
5898{
5899        GHashTable *t;
5900        gpointer old_key;
5901        gpointer old_val;
5902
5903        /* printf ("set (%s) %s to %s (%p)\n", class_name, key, value, e->class_data); */
5904        g_return_if_fail (class_name);
5905
5906        t = get_class_table_sure (e, class_name);
5907
5908        if (!g_hash_table_lookup_extended (t, key, &old_key, &old_val))
5909                old_key = NULL;
5910        else {
5911                if (strcmp (old_val, value))
5912                        g_free (old_val);
5913                else
5914                        return;
5915        }
5916        g_hash_table_insert (t, old_key ? old_key : g_strdup (key), g_strdup (value));
5917}
5918
5919void
5920html_engine_clear_class_data (HTMLEngine *e, const gchar *class_name, const gchar *key)
5921{
5922        GHashTable *t;
5923        gpointer old_key;
5924        gpointer old_val;
5925
5926        t = html_engine_get_class_table (e, class_name);
5927
5928        /* printf ("clear (%s) %s\n", class_name, key); */
5929        if (t && g_hash_table_lookup_extended (t, key, &old_key, &old_val)) {
5930                g_hash_table_remove (t, old_key);
5931                g_free (old_key);
5932                g_free (old_val);
5933        }
5934}
5935
5936static gboolean
5937remove_class_data (gpointer key, gpointer val, gpointer data)
5938{
5939        /* printf ("remove: %s, %s\n", key, val); */
5940        g_free (key);
5941        g_free (val);
5942
5943        return TRUE;
5944}
5945
5946static gboolean
5947remove_all_class_data (gpointer key, gpointer val, gpointer data)
5948{
5949        g_hash_table_foreach_remove ((GHashTable *) val, remove_class_data, NULL);
5950        /* printf ("remove table: %s\n", key); */
5951        g_hash_table_destroy ((GHashTable *) val);
5952        g_free (key);
5953
5954        return TRUE;
5955}
5956
5957void
5958html_engine_clear_all_class_data (HTMLEngine *e)
5959{
5960        if (e->class_data) {
5961                g_hash_table_foreach_remove (e->class_data, remove_all_class_data, NULL);
5962                g_hash_table_destroy (e->class_data);
5963                e->class_data = NULL;
5964        }
5965}
5966
5967const gchar *
5968html_engine_get_class_data (HTMLEngine *e, const gchar *class_name, const gchar *key)
5969{
5970        GHashTable *t = html_engine_get_class_table (e, class_name);
5971        return t ? g_hash_table_lookup (t, key) : NULL;
5972}
5973
5974static void
5975set_object_data (gpointer key, gpointer value, gpointer data)
5976{
5977        /* printf ("set %s\n", (const gchar *) key); */
5978        html_object_set_data (HTML_OBJECT (data), g_strdup ((const gchar *) key), g_strdup ((const gchar *) value));
5979}
5980
5981static void
5982html_engine_set_object_data (HTMLEngine *e, HTMLObject *o)
5983{
5984        GHashTable *t;
5985
5986        t = html_engine_get_class_table (e, html_type_name (HTML_OBJECT_TYPE (o)));
5987        if (t)
5988                g_hash_table_foreach (t, set_object_data, o);
5989}
5990
5991
5992HTMLEngine *
5993html_engine_get_top_html_engine (HTMLEngine *e)
5994{
5995        while (e->widget->iframe_parent)
5996                e = GTK_HTML (e->widget->iframe_parent)->engine;
5997
5998        return e;
5999}
6000
6001void
6002html_engine_add_expose  (HTMLEngine *e, gint x, gint y, gint width, gint height, gboolean expose)
6003{
6004        struct HTMLEngineExpose *r;
6005
6006        /* printf ("html_engine_add_expose\n"); */
6007
6008        g_assert (HTML_IS_ENGINE (e));
6009
6010        r = g_new (struct HTMLEngineExpose, 1);
6011
6012        r->area.x = x;
6013        r->area.y = y;
6014        r->area.width = width;
6015        r->area.height = height;
6016        r->expose = expose;
6017
6018        e->pending_expose = g_slist_prepend (e->pending_expose, r);
6019}
6020
6021static void
6022html_engine_queue_redraw_all (HTMLEngine *e)
6023{
6024        clear_pending_expose (e);
6025        html_draw_queue_clear (e->draw_queue);
6026       
6027        if (GTK_WIDGET_REALIZED (e->widget)) {
6028                gtk_widget_queue_draw (GTK_WIDGET (e->widget));
6029        }
6030}
6031
6032void
6033html_engine_redraw_selection (HTMLEngine *e)
6034{
6035        if (e->selection) {
6036                html_interval_unselect (e->selection, e);
6037                html_interval_select (e->selection, e);
6038                html_engine_flush_draw_queue (e);
6039        }
6040}
6041
6042void
6043html_engine_set_language (HTMLEngine *e, const gchar *language)
6044{
6045        g_free (e->language);
6046        e->language = g_strdup (language);
6047
6048        gtk_html_api_set_language (GTK_HTML (e->widget));
6049}
6050
6051const gchar *
6052html_engine_get_language (HTMLEngine *e)
6053{
6054        gchar *language;
6055
6056        language = e->language;
6057        if (!language)
6058                language = GTK_HTML_CLASS (GTK_WIDGET_GET_CLASS (e->widget))->properties->language;
6059        if (!language)
6060                language = "";
6061
6062        return language;
6063}
6064
6065static void
6066draw_link_text (HTMLText *text, HTMLEngine *e, gint offset)
6067{
6068        HTMLTextSlave *start, *end;
6069
6070        if (html_text_get_link_slaves_at_offset (text, offset, &start, &end)) {
6071                while (start) {
6072                        html_engine_queue_draw (e, HTML_OBJECT (start));
6073                        if (start == end)
6074                                break;
6075                        start = HTML_TEXT_SLAVE (HTML_OBJECT (start)->next);
6076                }
6077        }
6078}
6079
6080HTMLObject *
6081html_engine_get_focus_object (HTMLEngine *e, gint *offset)
6082{
6083        HTMLObject *o = e->focus_object;
6084        HTMLEngine *object_engine = e;
6085
6086        while (html_object_is_frame (o)) {
6087                object_engine = html_object_get_engine (o, e);
6088                o = object_engine->focus_object;
6089        }
6090
6091        if (o && offset)
6092                *offset = object_engine->focus_object_offset;
6093
6094        return o;
6095}
6096
6097static HTMLObject *
6098move_focus_object (HTMLObject *o, gint *offset, HTMLEngine *e, GtkDirectionType dir)
6099{
6100        if (HTML_IS_TEXT (o) && ((dir == GTK_DIR_TAB_FORWARD && html_text_next_link_offset (HTML_TEXT (o), offset))
6101                                 || (dir == GTK_DIR_TAB_BACKWARD && html_text_prev_link_offset (HTML_TEXT (o), offset))))
6102                return o;
6103
6104        o = dir == GTK_DIR_TAB_FORWARD
6105                ? html_object_next_cursor_object (o, e, offset)
6106                : html_object_prev_cursor_object (o, e, offset);
6107
6108        if (HTML_IS_TEXT (o)) {
6109                if (dir == GTK_DIR_TAB_FORWARD)
6110                        html_text_first_link_offset (HTML_TEXT (o), offset);
6111                else
6112                        html_text_last_link_offset (HTML_TEXT (o), offset);
6113        }
6114
6115        return o;
6116}
6117
6118gboolean
6119html_engine_focus (HTMLEngine *e, GtkDirectionType dir)
6120{
6121        if (e->clue && (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) {
6122                HTMLObject *cur;
6123                HTMLObject *focus_object;
6124                gint offset;
6125
6126                focus_object = html_engine_get_focus_object (e, &offset);
6127                if (focus_object && html_object_is_embedded (focus_object)
6128                    && HTML_EMBEDDED (focus_object)->widget
6129                    && gtk_widget_child_focus (HTML_EMBEDDED (focus_object)->widget, dir))
6130                        return TRUE;
6131
6132                if (focus_object)
6133                        cur = move_focus_object (focus_object, &offset, e, dir);
6134                else
6135                        cur = dir == GTK_DIR_TAB_FORWARD
6136                                ? html_object_get_head_leaf (e->clue)
6137                                : html_object_get_tail_leaf (e->clue);
6138
6139                while (cur) {
6140                        /* printf ("try child %p\n", cur); */
6141                        if ((HTML_IS_TEXT (cur) && html_object_get_complete_url (cur, offset))
6142                            || (HTML_IS_IMAGE (cur) && HTML_IMAGE (cur)->url && *HTML_IMAGE (cur)->url)) {
6143                                html_engine_set_focus_object (e, cur, offset);
6144
6145                                return TRUE;
6146                        } else if (html_object_is_embedded (cur) && !html_object_is_frame (cur)
6147                                   && HTML_EMBEDDED (cur)->widget) {
6148                                if (!GTK_WIDGET_DRAWABLE (HTML_EMBEDDED (cur)->widget)) {
6149                                        gint x, y;
6150
6151                                        html_object_calc_abs_position (cur, &x, &y);
6152                                        gtk_layout_put (GTK_LAYOUT (HTML_EMBEDDED (cur)->parent),
6153                                                        HTML_EMBEDDED (cur)->widget, x, y);
6154                                }
6155
6156                                if (gtk_widget_child_focus (HTML_EMBEDDED (cur)->widget, dir)) {
6157                                        html_engine_set_focus_object (e, cur, offset);
6158                                        return TRUE;
6159                                }
6160                        }
6161                        cur = move_focus_object (cur, &offset, e, dir);
6162                }
6163                /* printf ("no focus\n"); */
6164                html_engine_set_focus_object (e, NULL, 0);
6165        }
6166
6167        return FALSE;
6168}
6169
6170static void
6171draw_focus_object (HTMLEngine *e, HTMLObject *o, gint offset)
6172{
6173        e = html_object_engine (o, e);
6174        if (HTML_IS_TEXT (o) && html_object_get_url (o, offset))
6175                draw_link_text (HTML_TEXT (o), e, offset);
6176        else if (HTML_IS_IMAGE (o))
6177                html_engine_queue_draw (e, o);
6178}
6179
6180static void
6181reset_focus_object_forall (HTMLObject *o, HTMLEngine *e)
6182{
6183        if (e->focus_object) {
6184                /* printf ("reset focus object\n"); */
6185                if (!html_object_is_frame (e->focus_object)) {
6186                        e->focus_object->draw_focused = FALSE;
6187                        draw_focus_object (e, e->focus_object, e->focus_object_offset);
6188                }
6189                e->focus_object = NULL;
6190                html_engine_flush_draw_queue (e);
6191        }
6192
6193        if (o)
6194                o->draw_focused = FALSE;
6195}
6196
6197static void
6198reset_focus_object (HTMLEngine *e)
6199{
6200        HTMLEngine *e_top;
6201
6202        e_top = html_engine_get_top_html_engine (e);
6203
6204        if (e_top && e_top->clue) {
6205                reset_focus_object_forall (NULL, e_top);
6206                html_object_forall (e_top->clue, e_top, (HTMLObjectForallFunc) reset_focus_object_forall, NULL);
6207        }
6208}
6209
6210static void
6211set_frame_parents_focus_object (HTMLEngine *e)
6212{
6213        while (e->widget->iframe_parent) {
6214                HTMLEngine *e_parent;
6215
6216                /* printf ("set frame parent focus object\n"); */
6217                e_parent = GTK_HTML (e->widget->iframe_parent)->engine;
6218                e_parent->focus_object = e->clue->parent;
6219                e = e_parent;
6220        }
6221}
6222
6223void
6224html_engine_update_focus_if_necessary (HTMLEngine *e, HTMLObject *obj, gint offset)
6225{
6226        if (html_engine_get_editable(e))
6227                return;
6228
6229        if (obj && (((HTML_IS_IMAGE (obj) && HTML_IMAGE (obj)->url && *HTML_IMAGE (obj)->url))
6230                     || (HTML_IS_TEXT (obj) && html_object_get_complete_url (obj, offset))))
6231                html_engine_set_focus_object (e, obj, offset);
6232}
6233
6234void
6235html_engine_set_focus_object (HTMLEngine *e, HTMLObject *o, gint offset)
6236{
6237        /* printf ("set focus object to: %p\n", o); */
6238
6239        reset_focus_object (e);
6240
6241        if (o) {
6242                e = html_object_engine (o, e);
6243                e->focus_object = o;
6244                e->focus_object_offset = offset;
6245
6246                if (!html_object_is_frame (e->focus_object)) {
6247                        o->draw_focused = TRUE;
6248                        if (HTML_IS_TEXT (o))
6249                                HTML_TEXT (o)->focused_link_offset = offset;
6250                        draw_focus_object (e, o, offset);
6251                        html_engine_flush_draw_queue (e);
6252                }
6253                set_frame_parents_focus_object (e);
6254        }
6255}
6256
6257gboolean
6258html_engine_is_saved (HTMLEngine *e)
6259{
6260        return e->saved_step_count != -1 && e->saved_step_count == html_undo_get_step_count (e->undo);
6261}
6262
6263void
6264html_engine_saved (HTMLEngine *e)
6265{
6266        e->saved_step_count = html_undo_get_step_count (e->undo);
6267}
6268
6269static void
6270html_engine_add_map (HTMLEngine *e, const char *name)
6271{
6272        gpointer old_key = NULL, old_val;
6273 
6274        if (!e->map_table) {
6275                e->map_table = g_hash_table_new (g_str_hash, g_str_equal);
6276        }
6277
6278        /* only add a new map if the name is unique */
6279        if (!g_hash_table_lookup_extended (e->map_table, name, &old_key, &old_val)) {
6280                e->map = html_map_new (name);
6281               
6282                /* printf ("added map %s", name); */
6283
6284                g_hash_table_insert (e->map_table, e->map->name, e->map);
6285        }
6286}
6287
6288static gboolean
6289map_table_free_func (gpointer key, gpointer val, gpointer data)
6290{
6291        html_map_destroy (HTML_MAP (val));
6292        return TRUE;
6293}
6294
6295static void
6296html_engine_map_table_clear (HTMLEngine *e)
6297{
6298        if (e->map_table) {
6299                g_hash_table_foreach_remove (e->map_table, map_table_free_func, NULL);
6300                g_hash_table_destroy (e->map_table);
6301                e->map_table = NULL;
6302        }
6303}
6304
6305HTMLMap *
6306html_engine_get_map (HTMLEngine *e, const gchar *name)
6307{
6308        return e->map_table ? HTML_MAP (g_hash_table_lookup (e->map_table, name)) : NULL;
6309}
6310
6311struct HTMLEngineCheckSelectionType
6312{
6313        HTMLType req_type;
6314        gboolean has_type;
6315};
6316
6317static void
6318check_type_in_selection (HTMLObject *o, HTMLEngine *e, struct HTMLEngineCheckSelectionType *tmp)
6319{
6320        if (HTML_OBJECT_TYPE (o) == tmp->req_type)
6321                tmp->has_type = TRUE;
6322}
6323
6324gboolean
6325html_engine_selection_contains_object_type (HTMLEngine *e, HTMLType obj_type)
6326{
6327        struct HTMLEngineCheckSelectionType tmp;
6328
6329        tmp.has_type = FALSE;
6330        html_engine_edit_selection_updater_update_now (e->selection_updater);
6331        if (e->selection)
6332                html_interval_forall (e->selection, e, (HTMLObjectForallFunc) check_type_in_selection, &tmp);
6333
6334        return tmp.has_type;
6335}
6336
6337static void
6338check_link_in_selection (HTMLObject *o, HTMLEngine *e, gboolean *has_link)
6339{
6340        if ((HTML_IS_TEXT (o) && HTML_TEXT (o)->links) ||
6341            (HTML_IS_IMAGE (o) && (HTML_IMAGE (o)->url || HTML_IMAGE (o)->target)))
6342                *has_link = TRUE;
6343}
6344
6345gboolean
6346html_engine_selection_contains_link (HTMLEngine *e)
6347{
6348        gboolean has_link;
6349
6350        has_link = FALSE;
6351        html_engine_edit_selection_updater_update_now (e->selection_updater);
6352        if (e->selection)
6353                html_interval_forall (e->selection, e, (HTMLObjectForallFunc) check_link_in_selection, &has_link);
6354
6355        return has_link;
6356}
6357
6358gint
6359html_engine_get_left_border (HTMLEngine *e)
6360{
6361        return HTML_IS_PLAIN_PAINTER (e->painter) ? LEFT_BORDER : e->leftBorder;
6362}
6363
6364gint
6365html_engine_get_right_border (HTMLEngine *e)
6366{
6367        return HTML_IS_PLAIN_PAINTER (e->painter) ? RIGHT_BORDER : e->rightBorder;
6368}
6369
6370gint
6371html_engine_get_top_border (HTMLEngine *e)
6372{
6373        return HTML_IS_PLAIN_PAINTER (e->painter) ? TOP_BORDER : e->topBorder;
6374}
6375
6376gint
6377html_engine_get_bottom_border (HTMLEngine *e)
6378{
6379        return HTML_IS_PLAIN_PAINTER (e->painter) ? BOTTOM_BORDER : e->bottomBorder;
6380}
6381
6382void
6383html_engine_flush (HTMLEngine *e)
6384{
6385        if (!e->parsing)
6386                return;
6387
6388        if (e->timerId != 0) {
6389                g_source_remove (e->timerId);
6390                e->timerId = 0;
6391                while (html_engine_timer_event (e))
6392                        ;
6393        }
6394}
6395
6396HTMLImageFactory *
6397html_engine_get_image_factory (HTMLEngine *e)
6398{
6399        return e->image_factory;
6400}
6401
6402void
6403html_engine_opened_streams_increment (HTMLEngine *e)
6404{
6405        html_engine_opened_streams_set (e, e->opened_streams + 1);
6406}
6407
6408void
6409html_engine_opened_streams_decrement (HTMLEngine *e)
6410{
6411        html_engine_opened_streams_set (e, e->opened_streams - 1);
6412}
6413
6414void
6415html_engine_opened_streams_set (HTMLEngine *e, int value)
6416{
6417        e->opened_streams = value;
6418
6419        if (value == 0 && e->keep_scroll) {
6420                e->keep_scroll = FALSE;
6421                /*html_engine_calc_size (e, FALSE);
6422                  gtk_html_private_calc_scrollbars (e->widget, NULL, NULL);*/
6423                html_engine_schedule_update (e);
6424        }
6425}
6426
6427static void
6428calc_font_size (HTMLObject *o, HTMLEngine *e, gpointer data)
6429{
6430        if (HTML_IS_TEXT (o))
6431                html_text_calc_font_size (HTML_TEXT (o), e);
6432}
6433
6434void
6435html_engine_refresh_fonts (HTMLEngine *e)
6436{
6437        if (e->clue) {
6438                html_object_forall (e->clue, e, calc_font_size, NULL);
6439                html_object_change_set_down (e->clue, HTML_CHANGE_ALL);
6440                html_engine_calc_size (e, FALSE);
6441                html_engine_schedule_update (e);
6442        }
6443}
Note: See TracBrowser for help on using the repository browser.