source: trunk/third/gtkhtml/src/htmlengine.c @ 19188

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